13

Can you reverse a list of objects in Apex?

In Java and C#, there is a method you can call to reverse a list but I don't see one for Apex...

akcorp2003
  • 697
  • 3
  • 10
  • 26

6 Answers6

26

No such method currently exists on the List class as of Winter '17 (API v38). The only way to do this in Apex that I'm aware of is to loop over the list starting from the end.

List<Object> someList = new List<Object>{1,2,3,4,5};
List<Object> reversed = new List<Object>();

for(Integer i = someList.size() - 1; i >= 0; i--){ reversed.add(somelist[i]); }

If you're storing instances of an Apex class in a list, you could over-engineer a solution by using the strategy pattern and implementing the Comparable interface so you can use the sort() method with different sort orders.

A crude example

public class IntWrapper implements Comparable{
    public Integer int;
    public static String order = 'ASC';
public IntWrapper(Integer val){
    int = val;
}

public Integer compareTo(Object input){
    Integer result;
    if(IntWrapper.order == 'ASC'){
        result = compareASC(input);
    } else {
        result = compareDESC(input);
    }
    return result;
}

private Integer compareASC(Object input){
    Integer intIn = (Integer)input;
    if(int &lt; intIn){
        return -1;
    } else if(int &gt; intIn){
        return 1;
    } else {
        return 0;
    }
}

private Integer compareDESC(Object input){
    Integer intIn = (Integer)input;
    if(int &lt; intIn){
        return 1;
    } else if(int &gt; intIn){
        return -1;
    } else {
        return 0;
    }
}

}

List<IntWrapper> someList = new List<IntWrapper>{new IntWrapper(1), new IntWrapper(2), new IntWrapper(3), new IntWrapper(4), new IntWrapper(5)};

// First sort ensures we're in ASC order someList.sort();

// Set the static flag, sort again, et voila, list is reversed IntWrapper.order = 'DESC'; someList.sort();

Reversing insertion order like this is possible, but you'd need an extra integer in your class to hold the list index, and something like an addToList(someList) method that would set the list index to someList.size() before adding it to the list.

+edit:

This general method, having a wrapper class that implements Comparable, can also be used for defining custom sort orders for sObjects as well. There's even supporting documentation

Derek F
  • 61,401
  • 15
  • 50
  • 97
  • There is one extra minus sign in the for loop. :) It seems like the second solution is dependent on that the objects have some order to them. Would it work for a general solution where you just have a bunch of objects and you just want to flip the list? – akcorp2003 Nov 01 '16 at 20:59
  • Indeed there is. Seems like my keyboard liked to type a bunch of extra characters today. Answer edited. – Derek F Nov 01 '16 at 21:26
  • As for "just flip the list", I did make a note about that towards the bottom of my answer (regarding insertion order). You'd still need a wrapper class, with an Integer (let's call it order) to track the insertion order into the list. You wouldn't be able to simply add something to the list with list.add(), you'd need to write a method in the wrapper class (passing your target list as an argument) to set order and then insert into the list you passed as an argument. – Derek F Nov 01 '16 at 21:32
  • 1
    Honestly, just iterating backwards over the list in a loop is probably the simplest and fastest solution here. I just like working out these types of problems as programming exercises. – Derek F Nov 01 '16 at 21:35
  • 2
    A for loop going backwards is undoubtedly the easiest. I am curious as to why Salesforce hasn't implemented a reverse list method. – akcorp2003 Nov 02 '16 at 16:21
  • 1
    If I had to guess (since Apex is compiled to Java bytecode, ref 1, ref 2), I'd say it's because there probably isn't a good way for Salesforce to use Java's Collections.reverse(List<?>) with how I suspect they've implemented Lists in Apex. – Derek F Nov 02 '16 at 16:43
10

Just for completeness, you could also implement a custom reverse iterator :

public class ReverseIterator implements Iterator<Object>
{
    public List<Object> internalRef;
    integer position;
    public ReverseIterator(List<Object> source)
    {
        internalRef = source;
        position = internalRef.size();
    }

    public boolean hasNext()
    {
        return position > 0;
    }

    public Object next()
    {
        return internalRef[--position];
    }
}

Then to use:

ReverseIterator revIt = new ReverseIterator(new List<Object>{1, 2, 3});
while (revIt.hasNext())
{
    Integer current = (integer)revIt.next();
    System.debug(current);
}

At the moment, you can't use these in for loops, which limits the usefulness of actually implementing the interface, but perhaps in the future for loop support will be added to the interface.

IllusiveBrian
  • 4,288
  • 1
  • 14
  • 22
  • Thanks! This is definitely a neat solution and can work across whatever object I have in the list. This is just a tad more complicated. – akcorp2003 Nov 01 '16 at 21:02
  • 2
    @akcorp2003 Yeah, it would really shine if you could wrap it in a factory method and use it in a for loop like for(object i : reverseTraverse(myList)). One other use would be that you can create methods that take iterators and perform some set of operations on them, like LINQ extensions in C#. – IllusiveBrian Nov 02 '16 at 00:01
4

There is no "reverse" method in list, see all available methods: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_list.htm%23apex_methods_system_list

I guess you must create a new List and iterate over your existing list backwards populating the new one.

2

enter image description hereWhat about using a for loop and iterating in reverse?

Integer[] myInts = new Integer[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (Integer i = myInts.size()-1; i>= 0; i-- ) {
   System.debug('myInts[' + i + '] = ' + myInts[i]);
}
koma
  • 96
  • 1
  • 4
1

Not sure why the accepted answer suggests to create yet another list. Why not swap elements?

Integer n = someList.size();
for(Integer i=0, half=n>>1; i < half; i++) {
// or if you wish:
// for(Integer i=0; i < n/2; i++) {
    Object o = someList[i];
    someList[i] = someList[n-i-1];
    someList[n-i-1] = o;
}

An in-place reversion not only needs much less memory, it should also be faster. In the context of Apex it's often worthwhile to have a look how things are done in Java - e.g. have a look here.

Felix van Hove
  • 2,667
  • 4
  • 20
  • 26
0

If you just want the last element of a list created by a split in one line you can cheat a little:

System.debug('first/middle/last'.reverse().split('/').get(0).reverse());

This will get you the last element. You could also join a list before performing this magic trick.

Semmel
  • 2,445
  • 2
  • 20
  • 43