15

The title says it all. I insert a List into the database and want to pass a list of its Ids in the same order as in the original list. As this test proved using the Map and List constructors does not do the job.

Is there a one-liner so that I don't have to loop over a list?

List<SObject> sobjectList = new List<SObject>();
sobjectList.addAll(SmartFactory.createSObjectList('Contact', false, 2));
sobjectList.addAll(SmartFactory.createSObjectList('Account', false, 2));
sobjectList.addAll(SmartFactory.createSObjectList('Opportunity', false, 2));
insert sobjectList;

List<Id> idList = new List<Id>(new Map<Id, SObject>(sobjectList).keySet());

for(Integer i=0; i<sobjectList.size(); i++) {
    Id expectedId = sobjectList.get(i).Id;
    Id actualId = idList.get(i);
    System.assertEquals(expectedId, actualId);   
}

Note: When I say preserved order I DO NOT mean alphabetical order or anything like that. I mean it in that way that if SObject S1 is the first in the List, its Id also needs to be the first element in the id list.

7
  • Why don't you fill the list in the createSObjectList method? May 29, 2013 at 9:10
  • @Sergey - at the point where he's calling createSObjectList the objects haven't been inserted and so they don't have IDs...
    – Doug B
    May 29, 2013 at 9:28
  • @DougB Ahh right... Just overlooked insert over there :) May 29, 2013 at 9:40
  • Java lacks some sort of map functionality (in the sense of map-reduce) and Apex inherits that. You could write an utility class with a static method for frequently used mappings.
    – ipavlic
    May 29, 2013 at 10:45
  • 2
    You cant share the reason?
    – Phil Rymek
    May 29, 2013 at 23:12

2 Answers 2

4

Here goes the WTF...

In theory it has no loops (apart from the display loop). In reality I won't be surprised if it fails with "regex too complex" error on bigger dataset.

There should be a nicer way with Pattern, Matcher and negative look-behind but I'm too sober for this today...

String text;
List<Account> accs = new List<Account> {
    new Account(Name = 'Acc 1'), 
    new Account(Name = 'Acc 2'), 
    new Account(Name = 'Acc 3')
};

//  text = JSON.serialize(Database.insert(accs));
// if you don't want to insert, just pretend the next line is the result
text = '[{"id":"001A000000zBF1UIAW","success":true,"errors":[]},{"id":"001A000000zBF1VIAW","success":true,"errors":[]},{"id":"001A000000zBF1WIAW","success":true,"errors":[]}]';

text = text.replaceAll('"id":|"success":true|"errors":|\\[|\\]|\\{|\\}|\\"', '').replaceAll(',+', ',').removeEnd(',');
List<String> ids = text.split(',');

if(ids.size() != accs.size()){
    System.debug('Probably save has failed?');
} else {
    for(String s : ids){
        System.debug(s);
    }
}

I suspect it'll fail horribly when Database.insert(list, false) is used and one of rows fails. Up to you what you want in such scenario, null probably?

4
  • Weird, but cool ;) May 29, 2013 at 23:21
  • This is interesting, but I would be concerned about hitting heap space errors on larger strings with it.
    – Phil Rymek
    May 30, 2013 at 4:54
  • @PhilR If the insert is successful, LEN({"id":"001A000000zBF1UIAW","success":true,"errors":[]},) = 55 chars. Multiply by 200 max and you get bit over 10K while heap size is at least 3 MB. On failure of course it grows (don't know to how much, 200 * 1K? 5K sounds bit too long). It shouldn't be too bad on the heap, if anything I'd really suspect "regex too complicated" like salesforce.stackexchange.com/questions/10141/…, boards.developerforce.com/t5/Apex-Code-Development/…
    – eyescream
    May 30, 2013 at 6:21
  • 1
    if you can assume success (a terrible assumption), text.replace('[{"id":"','').replace('","success":true,"errors":[]}]','').split('","success":true,"errors":[]},{"id":"') will get it done without regex May 30, 2013 at 6:39
2

When you insert a list of sObjects all in one batch they will usually be assigned Ids sequentially. I don't think that this is guaranteed but in a testmethod where nothing else is happening concurrently I would expect it to always be the case.

In tried some quick tests with lists of various lengths and I wasn't able to get the records to insert out of order.

List<SObject> sobjectList = SmartFactory.createSObjectList('Contact', false, 5);
insert sobjectList;

List<Id> idList = new List<Id>(new Map<Id, SObject>(sobjectList).keySet());
idList.sort();

for(Integer i=0; i<sobjectList.size(); i++) {
    Id expectedId = sobjectList.get(i).Id;
    Id actualId = idList.get(i);
    System.assertEquals(expectedId, actualId);  
}
7
  • Really, the only difference between your code and mine is idList.sort(). But why in the world should I mix order by sorting it?! May 29, 2013 at 15:10
  • Sorting it fixes order, since a Map.keySet() is a set, in which order is not guaranteed.
    – Phil Rymek
    May 29, 2013 at 15:19
  • Is this really true?! I'm 99% sure that this just sorts alphabetical and its a technical coincidence that ids are that way. May 29, 2013 at 20:43
  • Greg: try it with fake ids or different objects and it will not work as sort has absolutelly nothing to do with preserving list order . May 29, 2013 at 20:47
  • 3
    @Robert no, not after you modified the question ;). May 29, 2013 at 22:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .