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 Decimal
s, 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)