Apex Array Slice

Here's a pattern that seems relatively efficient and easier to understand.

public static List<Object> slice(List<Object> input, Integer ge, Integer l)
{
    List<Object> output = input.clone();
    for (Integer i = 0; i < ge; i++) output.remove(0);
    Integer elements = l - ge;
    while (output.size() > elements) output.remove(elements);
    return output;
}

If you want to support negative index, just add these lines at the beginning of the method:

if (ge < 0) ge += input.size();
if (l < 0) l += input.size();

And running it through this script yields the proper input/output combos:

List<String> data = new List<String> { 'Banana', 'Orange', 'Lemon', 'Apple', 'Mango' };
List<String> sliced = (List<String>)slice(data, 1, 3);

system.debug(data); // (Banana, Orange, Lemon, Apple, Mango)
system.debug(sliced); // (Orange, Lemon)

List<String> data = new List<String> { 'zero', 'one', 'two', 'three' };
List<String> sliced = (List<String>)slice(data, 1, 3);

system.debug(data); // (zero, one, two, three)
system.debug(sliced); // (one, two)

While it's not a bad start, I can think of a number of problems:

  • No bounds checks are provided
  • Only Id and SObject are supported
  • SObject version returns generic list, does not support upsert
  • Has debug statements
  • Is overly verbose
  • Thrashes the heap more than necessary

Here's a version that closer to what I'd include in a library (which I'm working on), but feel free to study and borrow this code.

// Edit: now behaves better in more cases
public static Object[] slice(Object[] ary, Integer first, Integer last) {
    Object[] res = ary.clone(), temp;
    Integer size = ary.size(),
        startIndex = Math.min(size, Math.max(-1, first<0? size+first: first)),
        endIndex = Math.min(size, Math.max(-1, last<0? size+last: last-1)),
        offset = Math.max(-1, endIndex-startIndex);
    temp = new Object[offset+1];
    for(Integer h = 0, i = startIndex, j = endIndex; i <= j; ) {
        temp[h++] = ary[i++];
    }
    res.clear();
    res.addAll(temp);
    return res;
}

This version supports all generic lists, not just Id or SObject, returns a list of the same type as the original (so supports upsert), and has very similar behavior to JavaScript.


There's already some good answers here, but I figured I'd throw my hat into the ring anyways.

In my approach, I've tried to stay as close to the Javascript versions as possible.

The JS slice is a variable arity function that makes use of up to the first two arguments it is provided, so I provided three methods for my Slice class with 0, 1, and 2 parameters respectively.

Also, JS doesn't have integers, and all numbers are floating point, so I defined my method parameters as Decimals, and rounded with them the CEILING round mode; meaning that -0.5 is taken to be 0.

I wanted to name the parameters begin and end to stay inline with the mdn page, but those are unfortunately reserved (for future use) keywords.

I also went with a more OO approach instead of using static utility methods, but that should be easy enough to change if you want to do it the other way instead.

public class Slice {

    private list<Object> olist;

    public Slice(list<Object> olist){
        this.olist = olist;
    }   

    public list<Object> slice(){
        return olist.clone();
    }   

    public list<Object> slice(Decimal x_begin){
        Integer start = x_begin == NULL ? 0 : (Integer) x_begin.round(System.RoundingMode.CEILING);
        return commonSlice(start,olist.size());
    }   

    public list<Object> slice(Decimal x_begin, Decimal x_end){
        Integer start  = x_begin == NULL ? 0            : (Integer) x_begin.round(System.RoundingMode.CEILING);
        Integer finish = x_end   == NULL ? olist.size() : (Integer) x_end.round(System.RoundingMode.CEILING);
        return commonSlice(start, finish);
    }   

    private list<Object> commonSlice(Integer x_begin, Integer x_end){
        list<Object> ret = new list<Object>();
        x_begin = x_begin < 0 ? olist.size() + x_begin : x_begin ;
        x_end   = x_end   < 0 ? olist.size() + x_end   : x_end   ;   
        Integer maxIndex = olist.size();
        while (x_begin < maxIndex && x_begin < x_end){
            ret.add(olist[x_begin]);
            x_begin++;
        }
        return ret;
    }   


}

Some examples:

list<Integer> ilist = new list<Integer>{1,2,3,4,5};
Slice s = new Slice(ilist);
system.debug(s.slice());       // (1, 2, 3, 4, 5)
system.debug(s.slice(-.5,20)); // (1, 2, 3, 4, 5)
system.debug(s.slice(.5));     // (2, 3, 4, 5)
system.debug(s.slice(1));      // (2, 3, 4, 5)
system.debug(s.slice(2,-2));   // (3)
system.debug(s.slice(2,-3));   // ()
system.debug(s.slice(7,100));  // ()
system.debug(s.slice(NULL,2)); // (1, 2)

Tags:

Apex

Array