Teach Me Salesforce

A community approach to learning salesforce.com

Author Archive

Apex – Sorting a map

leave a comment »

So this is going to be one of the more apex heavy posts. This is a challenge I think many developers
have come across, and while what I propose is by no means the most elegant thing ever, it does do the
job until hopefully salesforce implements a native map sorting method. So here is the basic approach

1) populate map with information
2) create another map with the keys as the values you want to sort by, and the value as the key
for the other map.
3) create a list whos values are the keyset of the map created in step 2
4) sort the list
5) iterate over the list which now has your values in order, get the value from map 2 using the
current loop iterator as the key.

Sounds pretty complicated eh? It’s not SO bad once you kinda get the hang of it, but there is one
gotcha that kind of sucks, but we’ll cover that in a minute.

Here is a sample using a simple object called contestEntry. We’ll create a bunch of them with
random ordering, and then sort them and loop over the sorted result. You should be able to run this
in any org so you can see the principals in action.

        public class contestEntry
            public decimal rank{get;set;}
            public string name{get;set;}

        map<string,contestEntry> entries = new map<string,contestEntry>();

        contestEntry entry1 = new contestEntry();
        entry1.rank = 5;
        entry1.name = 'Frank';

        contestEntry entry2 = new contestEntry();
        entry2.rank = 3;
        entry2.name = 'Bob';

        contestEntry entry3 = new contestEntry();
        entry3.rank = 1;
        entry3.name = 'Jones';

        contestEntry entry4 = new contestEntry();
        entry4.rank = 4;
        entry4.name = 'Sandy';

        contestEntry entry5 = new contestEntry();
        entry5.rank = 2;
        entry5.name = 'Felix';

        //oh no, these entries are all out of order.
        system.debug(entries) ;

        //lets get sorting these guys. First we'll need a map to store the rank, and the contestEntry that rank is
        //associated with
        map<decimal,string> rankToNameMap = new map<decimal,string>();
        for(contestEntry entry : entries.values())
        //now lets put those ranks in a list
        list<decimal> ranksList = new list<decimal>();

        //now sort them

        //ok, so now we have the ranks in order, we need to figure out who had that rank
        for(decimal rank : ranksList)
            String thisEntryName = rankToNameMap.get(rank);
            contestEntry thisEntry = entries.get(thisEntryName);

Walking through it first we just make a sample object to use here. Normally this would be whatever you are actually trying to sort, but for the sake of easyness I just created an object called contestEntry. It just holds a name and a rank.

So then I make a map of those things, keyed by the persona name, and containing the contestEntry object. You might in real life have a map of sObjects keyed by their Id and containing the sObject itself. So then I make a bunch of those and add them to the map in random order so my sorting actually has some work to do 😛

The next thing is creating a map with the key of the value I want to sort this list by. The value is key of the first map. So in real life this might be a dollar amount on an opportunity and then the opportunity Id if the original map was a list of opportunities keyed by their Id.

We loop over all the objects in the original map, and add them to our temporary sorting map, again keyed by value to sort by, and value with key from original map.

Then we create a list of the type of the key of the temporary sorting map. Your key was a decimal? Then your list is of decimals as well, etc. Then add all the keys from the sorting map to the list you just made.

Sort the list.

Iterate over the sorted list, each entry in this list will be a key you can use to get the entry from the sorting map, which will contain the key to the original map. You now have a reference to your original map value by whatever value you sorted on.

There is however one gotcha with this approach. If you have duplicates in the value you are going to sort by, you are going to end up with collisions in your sorting map, which will end up with values overwriting each other and ultimately values missing in your final iteration. This happens because say in my example I have two people with rank 1. First person comes through, their rank is 1, and their name is Sandy. So the sorting map has 1=sandy. Then another person comes through, they also have rank 1 and their name is jones. Now the map has 1=jones. Sandy just fell out of the list. How do you deal with this? The best hack-ish fix I could come up with is to see if the key you are attempting to write to already exists, if so, then write to a slightly higher value key. Basically replace

       for(contestEntry entry : entries.values())


for(contestEntry entry : entries.values())
                decimal rank = entry.rank;
                    system.debug('------ INCRIMENTING Rank TOTAL FOR ' + entry.name);
                    rank += 0.001;


This won’t affect the value displayed when you retrieve and display the object, it just changes the sort order. I haven’t tested this throughly though so don’t rely on it extensively.

Anyway, there you have it Apex fans. A method to reliable (if not quickly or efficiently) sort Apex maps by just about anything. Once you understand the concepts it’s fairly easy to expand to sort to your hearts desire.
Originally posted by Kenji776 @ http://iwritecrappycode.wordpress.com/2011/09/28/apex-sorting-a-map/


Written by kenji776

September 28, 2011 at 5:07 pm

Apex update sobject without querying for it first

with 3 comments

This is just another quick handy tip. Sometimes when working with Apex you will want to update an object you already know the ID of without having to query for it first. Say for example you modify an account related to a contact. You already know the ID of the account, and don’t want to waste an SOQL query having to query for it. You might think you can do this

list<contact>  theContacts = [select id, accountid, name from contact limit 5];
list<account> updateAccounts = new list<account>();
for(contact con : theContacts)
    account thisContactsAccount = new account();
    thisContactsAccount.id = con.accountId;
    thisContactsAccount.name = 'blah blah blah';
update updateAccounts;

But you can’t. You’ll get the error ‘Field is not writeable: Account.Id’.
Instead, you can do this!

list<contact>  theContacts = [select id, accountid, name from contact limit 5];
list<account> updateAccounts = new list<account>();
for(contact con : theContacts)
    account thisContactsAccount = new account(Id=con.accountid);
    thisContactsAccount.name = 'blah blah blah';
update updateAccounts;

That will work and allow you to create a reference to an sObject without having to query for it first. Pretty handy sometimes. Anyway, just one of those quick time saving tips that isn’t super obvious at first. Hope this helps someone!

Written by kenji776

May 7, 2011 at 7:15 am

Posted in Apex, Beginner, Code Sample

Call Apex code from a custom button

with 3 comments

Want to be able to call an Apex class from a custom button on a detail page? I see this request show up somewhat frequently, and it is really quite useful. All you need to do is enable your apex class as a webservice, like so.

WebService static integer addNumbers(integer numOne, integer numTwo)
    return numOne + numTwo;

You can then invoke it via an execute javascript custom button.

var myvar = sforce.apex.execute("yourClass","addNumbers", {numOne:4,numTwo:5}");

Just replace the yourClass with the name of a class with the method you want to invoke.
You can pass arguments to the method by name, using the format you see above.
You can of course also pass in field values by using the field value
notation {!object.fieldName}. Remember to wrap text values in quotes.

Written by kenji776

May 2, 2011 at 1:52 pm

Posted in Apex, Intermediate

Tagged with , ,