14

I am currently facing an issue where an external system passes back an 18-digit Id that our platform has previously sent to them. However in the process the capitalization of the Id has been removed - read everything has become uppercase. I would imagine somebody has faced this problem before, but apparantly this is not the case.

So in short; is somebody aware of any existing (apex-) function that converts uppercased 18-digit Ids to a valid Salesforce Id?

Please beware; while a lot of answers exist for converting 15 to 18 digits, which is trivial, this is a different problem.

13

I ported Daniel Ballinger's answer here to Apex:

static String CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345';

static List<Boolean> getBitPattern(String c)
{
    Integer index = CHARS.indexOf(c.toUpperCase());
    List<Boolean> result = new List<Boolean>();
    for (Integer bitNumber = 0; bitNumber < 5; bitNumber++)
        result.add((index & (1 << bitNumber)) != 0);
    return result;
}
static String repairCasing(String input)
{
    if (String.isBlank(input) || input.length() != 18) return input;

    List<Boolean> toUpper = new List<Boolean>();
    toUpper.addAll(getBitPattern(String.valueOf(input.substring(15, 16))));
    toUpper.addAll(getBitPattern(String.valueOf(input.substring(16, 17))));
    toUpper.addAll(getBitPattern(String.valueOf(input.substring(17, 18))));

    String output = '';
    for (Integer i = 0; i < 15; i++)
    {
        String c = String.valueOf(input.substring(i, i+1));
        output += toUpper[i] ? c.toUpperCase() : c.toLowerCase();
    }
    output += input.substring(15, 18).toUpperCase();
    return output;
}

I tested it and it worked:

Id value1 = Id.valueOf('00129000007Kbn7AAC');
Id value2 = Id.valueOf('00129000007KBN7AAC');

system.assertEquals(value1, repairCasing(value2));
  • can you explain how toUpper[i] ? c.toUpperCase() : c.toLowerCase() works? I'm trying to recreate this in python now and I'm not familiar with that syntax – Jwok Nov 30 '18 at 1:16
  • 1
    FYI it's ternary syntax. In Python you'd use c.upper() if toUpper[i] else c.lower(). – Adrian Larson Nov 30 '18 at 21:34
  • 1
    got it figured out and posted it to this thread. I also created a new post on stack overflow here for a signal boost – Jwok Dec 1 '18 at 18:34
  • 2
    There's a typo on line 16 - should be input.substring(15, 16). This won't matter for most Ids (as most start with numbers) but any Ids that start with letters will have all those letters made lowercase. – YodaDaCoda May 31 at 1:42
  • 1
    Interesting. It did work before. I'll have to test it out before I update the post content. – Adrian Larson May 31 at 1:43
9

Parallel to Adrian's response I also cooked up my own version for reference;

public static Id correctCapitalizedId(String input) {
    String keyString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345', result = '';
    Integer i = 0, s = 15;

    if (input.length() != 18) {
        return null;
    }

    for (String seq : input.split('(?<=\\G.{5})')) {
        Integer chk = keyString.indexOf(input.mid(s++,1));
        i = 0;
        for (String chr : seq.split('')) {
            result += seq.length() != 5  ? '' : (chk & (i == 0 ? ++i : (i*=2))) == 0 ? chr.toLowerCase() : chr;
        }
    }
    return Id.valueOf(result);
}
  • 1
    Elegant. Way to take the cleanup a step further! – Adrian Larson May 24 '16 at 20:37
  • 2
    Had to edit my code slightly - it was not working correctly in the sense that the last 3 characters were always lowercased - this causes Id.valueOf to render an Id - but subsequently actually using this Id while trying to populate/commit a lookup-field will still render a nasty error.... – Koen Faro May 24 '16 at 21:46
1

Given that the 18 character form of an ID is designed to be a unique identifier irrespective of the casing, I'm not sure why you would ever need to convert to a 15 digit ID.

Why not stay with the 18 character values? If you did for example need to use them as string map keys just normalise their case by using toLowerCase() on them. They remain valid ID values and can be converted to the type Id using Id.valueOf.

  • 2
    Purpose here was actually querying for the record indeed! – Koen Faro May 24 '16 at 20:40
  • 1
    Thats what I assumed too @KeithC, until I noticed that my test-case only had uppercase characters to start with and things broke down in production! – Koen Faro May 24 '16 at 20:50
  • 1
    @KoenFaro OK I did a bit more reading and I get it now. Correctly cased 18 chars or 15 chars work in queries but if case is lost code needs running to use the extra info in the extra 3 character os the 18 chars to recover the correct casing of the 15 chars. Seems like a method that should be present on the Id class but isn't. – Keith C May 24 '16 at 21:01
  • 1
    I would actually expect it to be part of the native Id.valueOf function, rather strange that SF skipped implementing the few lines of code above, sounds like a rush job of adding 18 character Ids - if you open the idea I'll vote! – Koen Faro May 24 '16 at 21:09
  • 1
    @KoenFaro If I believed opening developer-oriented ideas made any difference I would... – Keith C May 24 '16 at 21:27
1

In case anyone else is using it, I took Adrian Larson's apex code and converted it to Python.

def getBitPatterns(c):
    CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ012345'
    index = CHARS.find(c)
    result = []
    for bitNumber in range(0,5):
        result.append((index & (1 << bitNumber)) != 0)
    return result

def repairCasing(inp):
    if len(inp) < 18:
        return 'Error'
    toUpper = []
    toUpper.append(getBitPatterns(inp[15:16]))
    toUpper.append(getBitPatterns(inp[16:17]))
    toUpper.append(getBitPatterns(inp[17:18]))
    toUpper = [item for sublist in toUpper for item in sublist]

    output = ''

    for i in range(0,15):
        c = inp[i:i+1]
        if toUpper[i]:
            output += c.upper()
        else:
            output += c.lower()

    output += inp[15:18].upper()

    return output
  • 1
    I think you can get the last 12 lines down to one with a list comprehension if you like. return ''.join([x.upper() if x in toUpper else x.lower() for x in inp[:15]] + inp[15:].upper() – David Reed Nov 30 '18 at 19:24
  • I messed it up, of course. in toUpper should just be toUpper[x]. – David Reed Dec 1 '18 at 20:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.