3

Does anyone have an idea of the distance between two Salesforce Ids of the same object and instance?

Example Account Id:

Object-Instance-Reserved-This Distance

001-30-0-0000xxxxx

  • 2
    Though I do not know the answer but I am curious to know why you want to find this – Saumya Ranjan Satapathy Jan 23 '17 at 17:02
  • 3
    I'm also curious as to why you want this information, and what you plan to do with it. This sounds like an X-Y problem to me. While I'm here, what do you mean by 'distance' between two Ids? For example, is the distance between '001xxxxxxx12345' and '001xxxxxxx1235a' 2 (the Levenshtein distance) or 36 (number of Ids between the two, taking Ids as numbers in a base-36 system)? – Derek F Jan 23 '17 at 17:56
  • What is your meaning with distance. These are in sequence. – Ashwani Jan 23 '17 at 17:56
  • @Ashwani Yes, they are sequenial, but there are "gaps". Is the distance 256 until the next Id, or 64? – All about the W Jan 23 '17 at 18:22
  • 3
    Where are you getting 256 and 64? Please edit your post to be more specific and clear, and if possible actually explain your end goal at a high level. – Adrian Larson Jan 23 '17 at 19:10
2

As per What are Salesforce ID's composed of?, the Ids are base-62 encoded. If you convert the base62 representation to a decimal representation then calculating the decimal "distance" between the two records would be easy enough.

Lets create a few test records to get a feel for how they run in a sequence:

List<Account> accountsToCreate = new List<Account>();
for(Integer i = 0; i < 62 * 4; i++) {
    accountsToCreate.add(new Account(Name = 'Account:' + i));
}
insert accountsToCreate;
for(Account acc : accountsToCreate) {
    System.debug(acc.Id);
}

delete accountsToCreate;

An abbreviated version of the output:

0017000001WXV8y
0017000001WXV8z
0017000001WXV90
0017000001WXV91
0017000001WXV92
... 3 to 8
0017000001WXV99
0017000001WXV9A
... B to X
0017000001WXV9Y
0017000001WXV9Z
0017000001WXV9a
0017000001WXV9b
... c to x
0017000001WXV9y
0017000001WXV9z
0017000001WXVA0
0017000001WXVA1
0017000001WXVA2
... 3 to 8
0017000001WXVA9
0017000001WXVAA
... B to Y
0017000001WXVAZ
0017000001WXVAa
... b to x
0017000001WXVAy
0017000001WXVAz
0017000001WXVB0
0017000001WXVB1
... 2 to 8
0017000001WXVB9
0017000001WXVBA
... B to X
0017000001WXVBY
0017000001WXVBZ
0017000001WXVBa
... b to x
0017000001WXVBy
0017000001WXVBz
0017000001WXVC0
...
0017000001WXVC1
...

The sequence is clear enough, run through 0 to 9, then A to Z, then finally a to z before wrapping round and increment the higher characters.

What we now need is a way to do base62 decoding in Apex. Turns out I did something similar way back in 2011 except it was in T-SQL rather than Apex.

public class IdDistance {

    // This is the order ID's were assigned in when tested
    final static string base62Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

    public static long numericDistance(Id firstId, Id secondId) {
        long firstNumericId = numericId(firstId);
        long secondNumericId = numericId(secondId);
        return secondNumericId - firstNumericId;
    }

    public static long numericId(Id input) {
        return numericId((string)input);
    }

    public static long numericId(string input) {

        string idAsString = (string)input;
        if(idAsString.length() > 15) {
            // Drop the case checking suffix for the last 3 characters of an 18 char ID.
            idAsString = idAsString.substring(0, 15);
        }   

        long returnValue = 0;  
        long multiplier = 1;    

        for(integer i = idAsString.length(); i > 1; i--) {
            // The character being converted
            string idChar = idAsString.substring(i-1, i);  
            System.debug(idChar);
            // The index of the character being converted
            long value = base62Chars.indexOf(idChar);

            returnValue = returnValue + ( value * multiplier );  
            multiplier = multiplier * 62;  
        }

        return returnValue;
    }
}

Test class. Add more are required.

@IsTest
public class IdDistance_Test {
    @IsTest
    public static void testValues() {
        System.assertEquals(0, IdDistance.numericId('000'));
        System.assertEquals(1, IdDistance.numericId('001'));
        System.assertEquals(10, IdDistance.numericId('00A'));
        System.assertEquals(35, IdDistance.numericId('00Z'));
        System.assertEquals(36, IdDistance.numericId('00a'));
        System.assertEquals(61, IdDistance.numericId('00z'));
        System.assertEquals(62, IdDistance.numericId('010'));

        Id testId = '00Q7000001DsqIj';
        System.assertEquals(911562501854070361L, IdDistance.numericId(testId));

    }

    @IsTest
    public static void distance() {
        System.assertEquals(1, IdDistance.numericDistance('00Q7000001DsqIj', '00Q7000001DsqIk'));
        System.assertEquals(10, IdDistance.numericDistance('00Q7000001DsqI1', '00Q7000001DsqIB'));
    }

}

Sample output:

'0' > 0
'1' > 1
'A' > 10
'Z' > 35
'a' > 36
'z' > 61
'10' > 62

Remember to truncate off any extra characters after the first 15, as we don't want the case checking suffix. Special consideration might also be required for Orgs that have gone through a pod migration.


Of course, even though you can now do this, the question still remains of why you would want to do this.

  • Thank you for the detailed response, this is exactly what I was looking for. A southern defense contractor (my customer) has a use case for this. – All about the W Apr 7 '17 at 16:32

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.