将Salesforce 15个字符的ID转换为18个字符


20

Salesforce CRM中,每个对象都有15个字符的字母数字ID,区分大小写。如果有人好奇,实际上它是62的基数。但是,用于数据迁移和集成的工具可能支持也可能不支持区分大小写。为了克服这个问题,可以将ID安全地转换为18个字符的不区分大小写的字母数字ID。在该过程中,将3个字符的字母数字校验和添加到ID。转换算法为:

范例

a0RE000000IJmcN
  1. 将ID分成三个5个字符的块。

    a0RE0  00000  IJmcN
    
  2. 反转每个块。

    0ER0a  00000  NcmJI
    
  3. 1如果每个字符都用大写字母替换,0否则用大写字母替换。

    01100  00000  10011
    
  4. 对于每个5位二进制数i,在位置获取字符i在大写字母和数字0-5(ABCDEFGHIJKLMNOPQRSTUVWXYZ012345)串联的。

    00000 -> A,
    00001 -> B,
    00010 -> C, ..., 
    11010 -> Z, 
    11011 -> 0, ...,
    11111 -> 5`
    

    屈服:

    M  A  T
    
  5. 将这些字符(校验和)附加到原始ID。

输出量

a0RE000000IJmcNMAT

编写以15个字符的字母数字(ASCII)字符串作为输入并返回18个字符的ID的程序或函数。

输入验证不在此问题的范围内。程序可能会返回任何值,或者由于无效输入而崩溃。

请不要使用使该挑战变得微不足道的Salesforce专有语言的功能(例如,公式CASESAFEID(),转换IdString在APEX&c中 Formula)。

测试用例

a01M00000062mPg    -> a01M00000062mPgIAI
001M000000qfPyS    -> 001M000000qfPySIAU
a0FE000000D6r3F    -> a0FE000000D6r3FMAR
0F9E000000092w2    -> 0F9E000000092w2KAA
aaaaaaaaaaaaaaa    -> aaaaaaaaaaaaaaaAAA
AbCdEfGhIjKlMnO    -> AbCdEfGhIjKlMnOVKV
aBcDEfgHIJKLMNO    -> aBcDEfgHIJKLMNO025

3
可悲的是,在Apex代码中将字符串转换为ID仍然不会比此处提供的某些答案短,尤其是在代码必须独立的情况下。Apex Code不太适合打高尔夫球。
phyrfox

2
@phyrfox作为前Salesforce开发人员。Apex不太适合...
Mike McMahon

2
APEX,56个字节:public class X{public X(Id i){System.debug((String)i);}}。不过,仅适用于有效的Salesforce ID。
Trang Oul

我来到这里的目的是为了实际工作success.jitterbit.com/display/DOC/…,而不是高尔夫,但是我对算法的描述有些困惑。您说步骤4中的每个反向和消毒的块都是一个“二进制数”,但是您绝不能用0和1替换数字2-8。当在“ 62mPg”之类的步骤1-3上产生“ 01026”之类的数字时,我应该对第4步做什么?
K。。

Answers:


6

Ruby,97个字节

->s{s+s.scan(/.{5}/).map{|x|[*?A..?Z,*?0..?5][x.reverse.gsub(/./){|y|y=~/[^A-Z]/||1}.to_i 2]}*''}
->s{               # define an anonymous lambda
s+                 # the original string plus...
s.scan(/.{5}/)     # get every group of 5 chars
.map{|x|           # map over each group of 5 chars...
[*?A..?Z,*?0..?5]  # build the array of A-Z0-5
[                  # index over it with...
x.reverse          # the 5-char group, reversed...
.gsub(/./){|y|     # ... with each character replaced with...
y=~/[^A-Z]/||1     # ... whether it's uppercase (0/1)...
}.to_i 2           # ... converted to binary
]                  # (end index)
}*''               # end map, join into a string
}                  # end lambda

这有一些非常巧妙的技巧。

我最初将字符串分为5个字符的本能是each_slice

irb(main):001:0> [*1..20].each_slice(5).to_a
=> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]]

事实证明,与简单的正则表达式(x.chars.each_slice(5)vs. x.scan(/.{5}/))相比,这太长了。事后看来,这似乎很明显,但我从未真正考虑过……也许我可以在这里优化一些我以前的Ruby答案。

我最自豪的是这段代码:

y=~/[^A-Z]/||1

好的,这是非Ruby爱好者的一些背景知识。Ruby完全将布尔值(TrueClassFalseClass)与整数/数字(Numeric)分隔开-这意味着也不会自动将true从true转换为1,将false转换为0。打高尔夫球时这很烦人(但这是一件好事……出于其他所有目的)。

检查单个字符是否为大写(并返回1或0)的幼稚方法是

y.upcase==y?1:0

我们可以进一步讲解(再次使用正则表达式):

y=~/[A-Z]/?1:0

但是后来我真的开始思考了。Hmm ... =~返回匹配的索引(因此,对于我们的单个字符,始终0在存在匹配的情况下),或者nil在匹配失败时返回虚假的值(FalseClassRuby中的其他所有内容除外)。如果||运算符为真,则运算符采用其第一个操作数,否则为第二个操作数。因此,我们可以打高尔夫球到

y=~/[^A-Z]/||1

好吧,让我们看看这里发生了什么。如果y是大写字母,则将不匹配[^A-Z],因此regex部分将返回nilnil || 11,因此大写字母变为1。如果y不是大写字母,则正则表达式部分将返回0(因为index处有一个匹配项0),并且由于0为真,所以0 || 10

...并且只有在全部写完这些之后,我才意识到这实际上与y=~/[A-Z]/?1:0。哈哈,好吧。


6

Pyth,23 22字节

FryAmTheEggman保存了1个字节。

sm@s+JrG1U6i}RJ_d2c3pz

在线尝试。 测试套件。

这可能是我第一次p在高尔夫球中使用rint指令。

说明

     JrG1                   save uppercase alphabet in J
                     z      input string
                    p       print it without newline
                  c3        split into 3 parts
 m              d           for each part:
               _              reverse
            }R                map characters to being in
              J                 uppercase alphabet (saved in J)
           i     2            parse list of bools as binary
  @                           get correct item of
     J                          uppercase alphabet (saved in J)
   s+    U6                     add nums 0-5 to it
s                           concatenate and print

4

MATL,24字节

j1Y24Y2hG5IePtk=~!XB1+)h

使用语言/编译器的当前版本(9.1.0)

例子

>> matl
 > j1Y24Y2hG5IePtk=~!XB1+)h
 >
> a0RE000000IJmcN
a0RE000000IJmcNMAT

>> matl
 > j1Y24Y2hG5IePtk=~!XB1+)h
 >
> a01M00000062mPg
a01M00000062mPgIAI

说明

j            % input string
1Y2          % predefined literal: 'ABC...Z'
4Y2          % predefined literal; '012...9'
h            % concatenate into string 'ABC...Z012...9'
G            % push input string
5Ie          % reshape into 5x3 matrix, column-major order
P            % flip vertically
tk=~         % 1 if uppercase, 0 if lowercase
!XB1+        % convert each column to binary number and add 1
)            % index 'ABC...Z012...9' with resulting numbers
h            % concatenate result with original string

3

JavaScript(ES6),108

x=>x.replace(/[A-Z]/g,(x,i)=>t|=1<<i,t=0)+[0,5,10].map(n=>x+='ABCDEFGHIJKLMNOPQRSTUVWXYZ012345'[t>>n&31])&&x

测试

f=x=>x.replace(/[A-Z]/g,(x,i)=>t|=1<<i,t=0)+[0,5,10].map(n=>x+='ABCDEFGHIJKLMNOPQRSTUVWXYZ012345'[t>>n&31])&&x

// Less golfed

U=x=>{
  x.replace(/[A-Z]/g,(x,i)=>t|=1<<i,t=0); // build a 15 bit number (no need to explicit reverse)
  // convert 't' to 3 number of 5 bits each, then to the right char A..Z 0..5
  [0,5,10].forEach(n=> // 3 value for shifting
    x += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345' // to convert value to char
     [ t>>n&31 ] // shift and mask
  );
  return x
}

console.log=x=>O.innerHTML+=x+'\n';

;[
  ['a01M00000062mPg','a01M00000062mPgIAI']
, ['001M000000qfPyS','001M000000qfPySIAU']
, ['a0FE000000D6r3F','a0FE000000D6r3FMAR']
, ['0F9E000000092w2','0F9E000000092w2KAA']
, ['aaaaaaaaaaaaaaa','aaaaaaaaaaaaaaaAAA']
, ['AbCdEfGhIjKlMnO','AbCdEfGhIjKlMnOVKV']
, ['aBcDEfgHIJKLMNO','aBcDEfgHIJKLMNO025']
].forEach(t=>{
  var i=t[0],x=t[1],r=f(i);
  console.log(i+'->'+r+(r==x?' OK':' Fail (expected '+x+')'));
})
<pre id=O></pre>


2

CJam,27个字节

l_5/{W%{_el=!}%2bH+43%'0+}%

运行所有测试用例。

规范的相当直接的实现。最有趣的部分是在校验和中转换为字符。我们将17加到每个块的结果中。取该模数43并将其结果添加到字符中'0


2

Japt,46个字节

U+U®f"[A-Z]" ?1:0} f'.p5)®w n2 +A %36 s36 u} q

对长度不太满意,但是我找不到打高尔夫球的方法。 在线尝试!


2

JavaScript(ES6), 137 132字节

s=>s+s.replace(/./g,c=>c>"9"&c<"a").match(/.{5}/g).map(n=>"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"[0|"0b"+[...n].reverse().join``]).join``

@ՊՓԼՃՐՊՃՈԲՍԼ节省了4个字节!

说明

这个挑战根本不适合JavaScript。没有逆向转换字符串的简短方法,而且看起来将数字转换为字符的最简单方法是对每个可能的字符进行硬编码。

s=>
  s+                                   // prepend the original ID
  s.replace(/./g,c=>c>"9"&c<"a")       // convert each upper-case character to 1
  .match(/.{5}/g).map(n=>              // for each group of 5 digits
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"
    [0|"0b"+                            // convert from binary
      [...n].reverse().join``]          // reverse the string
  ).join``

如果允许校验和中的数字小写,则可以用124个字节来完成,如下所示:

s=>s+s.replace(/./g,c=>c>"9"&c<"a").match(/.{5}/g).map(n=>((parseInt([...n].reverse().join``,2)+10)%36).toString(36)).join``

测试


如果我没记错的话,parseInt([...n].reverse().join``,2)可以更改为+`0b${[...n].reverse().join``}`
Mama Fun Roll

@ՊՓԼՃՐՊՃՈԲՍԼ你是对的!我也节省了一个字节,谢谢。
user81655'1

使用保存10个完整字节.replace(/.{5}/g,n=>/*stuff*/)
尼尔

2

MATLAB,100 98字节

s=input('');a=flip(reshape(s,5,3))';e=['A':'Z',48:53];disp([s,e(bin2dec(num2str(a~=lower(a)))+1)])

将请求一个字符串作为输入,并且输出将显示在屏幕上。

说明

我可能在这里使用最简单的方法:

  • 要求输入
  • 重塑为5(行)x 3(列)
  • 翻转行顺序
  • 转置矩阵以准备将其读取为二进制
  • 分配ABC ... XYZ012345数组
  • 将转置矩阵的字符索引与其等效的小写字母进行比较,然后将布尔值转换为字符串,然后将其读取为二进制并转换为十进制。
  • 将这些十进制小数(递增1)解释为已分配数组的索引。
  • 显示输入的其他3个字符

现在不到100个字节,这要感谢Luis Mendo!


1
您可以使用一点节省e=['A':'Z',48:53]
Luis Mendo

我认为我的方法与您的方法几乎相同:-)
Luis Mendo

2

PHP,186181字节

<?$z=$argv[1];$x=str_split($z,5);$l="ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";foreach($x as$y){foreach(str_split(strrev($y))as$a=>$w)$y[$a]=ctype_upper($w)?1:0;$z.=$l[bindec($y)];}echo $z;

不戴手套

<?php
$z = $argv[1];
$x = str_split($z,5);
$l = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345";
foreach($x as $y) {
    foreach( str_split( strrev($y) ) as $a => $w) {
        $y[$a] = ctype_upper($w) ? 1 : 0;
    }
    $z .= $l[bindec($y)];
}
echo $z;

我开始以为我可以使它更短一些,但是我没有办法使它变得更短。


1

Python 2,97个字节

lambda i:i+''.join(chr(48+(17+sum((2**j)*i[x+j].isupper()for j in range(5)))%43)for x in[0,5,10])

1

PowerShell,162字节

function f{param($f)-join([char[]](65..90)+(0..5))[[convert]::ToInt32(-join($f|%{+($_-cmatch'[A-Z]')}),2)]}
($a=$args[0])+(f $a[4..0])+(f $a[9..5])+(f $a[14..10])

好吧,这其中发生了很多整洁的事情。我将从第二行开始。

我们通过将输入作为字符串$args[0]并将其设置$a为以后使用。它被封装在其中,()以便执行并返回结果(即$a),因此我们可以立即将它与三个函数调用的结果进行字符串连接(f ...)。每个函数调用将以相反顺序分块索引的输入字符串作为一个字符数组作为参数传递-对于示例输入而言,这$a[4..0]将等于@('0','E','R','0','a')与每个条目作为一个char,而不是一个字符串。

现在到函数,程序的真正作用所在。我们将输入作为$f,但是仅在最后使用它,因此让我们首先关注那里。由于它作为一个char数组传递(由于我们之前的索引编制),我们可以立即通过将其传递到循环中$f|%{...}。在循环内,我们获取每个字符并执行区分大小写的regex匹配,-cmatch如果匹配为大写/否则将返回true / false。我们使用encapsulation将其强制转换为整数+(),然后-join编辑1和0的数组以形成字符串。也就是说然后作为在.NET第一个参数传递[convert]::ToInt32()呼叫改变进制(base2)转换成十进制数。我们使用所得的十进制数索引到字符串(-join(...)[...])。字符串首先被公式化为一个转换(65..90)为字符数组的范围,然后与该范围串联(0..5)(即,字符串为"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345")。所有这些都是从字符串中返回适当的字符。


1

Jolf,30个字节

最后,可能还不错!在这里尝试!

+i mZci5d.p1CρA_Hpu1"[^1]'0"2
    Zci5                      split input into groups of 5
  _m                          map it
        d                      with this function
               _H              reverse H
              A  pu1            and replace in it all uppercase letters with 1
             ρ      "[^1]'0"    replace all non-ones with zeroes
            C               2   parse as binary integer
         .p1                    get the (^)th member of "A...Z0...9"

1

Python 3中,201个174 138字节

非常感谢Trang Oul指出了不再需要的函数声明。和Python三元运算符。和一些不正确的输出。只是...只给他投票。

i=input();n='';c=l=15;
while c:c-=1;n+=('0','1')[i[c].isupper()]
while l:v=int(n[l-5:l],2);l-=5;i+=(chr(v+65),str(v-26))[v>25]
print(i)

z()只需使用一次函数,即可替换其调用并节省25个字节。另外,您的代码错误地分配了[而不是0
Trang Oul

好吧,这对我来说是一个令人尴尬的监督。谢谢。
史蒂夫·埃克特

1
您可以节省更多,方法是先if else此构造替换,再用三元运算符替换。
Trang Oul


1

C, 120 118字节

n,j;main(c,v,s)char**v,*s;{for(printf(s=v[1]);*s;s+=5){for(n=0,j=5;j--;)n=n*2+!!isupper(s[j]);putchar(n+65-n/26*17);}}

适用于任何长度为5的倍数的输入:)

不打高尔夫球

n,j;

main(c,v,s) char **v, *s;
{
    for(printf(s = v[1]); *s; s+=5)
    {
        for(n=0, j=5; j--;)
            n=n*2+!!isupper(s[j]);

        putchar(n+65-n/26*17);
    }
}

为了节省一些字节,如果使用main(n,v,s)作为签名,则可以从全局名称空间中删除n,因为否则就不使用argc。
cleblanc

还要用普通的旧442替换26 * 17,再保存一个字节
cleblanc 2016年

经过更多编辑后,您的版本降低到110个字节。我不明白为什么isupper()对我来说很好用,所以您为什么拥有!! isupprer()。我还重构了您的for循环,以删除一些不必要的内容{} j;main(n,v,s)char**v,*s;{for(printf(s=v[1]);*s;s+=5,putchar(n+65-n/442))for(n=0,j=5;j--;n=n*2+isupper(s[j]));}
cleblanc 2016年

@cleblanc非常好的建议,非常感谢。操作顺序在n/26*17表达式因此用442代替不是一个选择。至于!!isupper,该函数在我的系统上没有为true返回1,而是返回256。!!无论如何,这都是将其转换为0/1返回值的简便方法。YMMV。
科尔·卡梅隆

1

C#,171个字节

我在C#高尔夫方面不是很熟练,但是这里有一个机会。

s=>{for(var u=s;u.Length>0;u=u.Substring(5)){int p=0,n=u.Substring(0,5).Select(t=>char.IsUpper(t)?1:0).Sum(i=>(int)(i*Math.Pow(2,p++)));s+=(char)(n+65-n/26*17);}return s;}

意见建议:char.IsUpper(t)可以替换为t>=65&t<=90&在C#中,布尔型基本上是&&没有短路的高尔夫球短裤)。447比短26*17。您不需要单独做Select:您可以将三元数直接包含在Sum。考虑使用代替Substring基于的所有这些用法Take,例如for(int i=0;i<3;i++)s.Skip(i*5).Take(5)。以供将来参考,它u!=""会比u.Length>0()短(但如果您正在使用,则不再需要Take)。
鲍勃

该表达式n/26*17不等于n/442,但除此之外,感谢您的建议。如前所述,我在C#中打高尔夫球的经验不是很丰富,所以这对我来说是以后考虑的全部。
科尔·卡梅隆

啊,对不起-我看错了。
鲍勃

1

C#334

string g(string c){string[]b=new string[]{c.Substring(0,5),c.Substring(5, 5),c.Substring(10)};string o="",w="";for(int i=0,j=0;i<3;i++){char[]t=b[i].ToCharArray();Array.Reverse(t);b[i]=new string(t);o="";for(j=0;j<5;j++){o+=Char.IsUpper(b[i][j])?1:0;}int R=Convert.ToInt32(o,2);char U=R>26?(char)(R+22):(char)(R+65);w+=U;}return c+w;}

如果需要,我将代码反转为可读并发布。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.