0

How can I do like findIndex from List in Apex?

I wanna do like below.

public class SelectItem {
    public String Text { get; set; }
    public String Value { get; set; }
}

public void FooMethod() {
    List<SelectItem> itemList = new List<SelectItem>();
    for (Integer i = 0; i < 5; i++) {
        SelectItem item = new SelectItem();
        item.Text = 'test' + String.valueOf(i);
        item.Value = String.valueOf(i);
        itemList.add(item);
    }
    
    // I wanna do↓
    Integer targetIndex = itemList.findIndex(x => x.Text == 'test2');
    SelectItem targetItem = itemList[targetIndex];
}
1

The arrow functions don't exist in Apex. This is plain JavaScript.

You can conceptually find an element if you already have it:

SelectOption[] options = 
    new SelectOption[] {
    new SelectOption('a','a'),
    new SelectOption('b','b')
};
SelectOption target = new SelectOption('b','b');
System.assertEquals(1, options.indexOf(target));

This operation is basically dependent on all fields being the same. In other cases, you probably won't be able to use this; you'll need to come up with a custom solution.

1

If you always use the same (partial match) criteria for finding values (here you match the Text value), the best solution is to actually use a Map not a List. Something like this:

public class SelectItem {
    public String Text { get; set; }
    public String Value { get; set; }
}

public void FooMethod() {
    Map<String, SelectItem> items = new Map<String, SelectItem>();
    for (Integer i = 0; i < 5; i++) {
        SelectItem item = new SelectItem();
        item.Text = 'test' + String.valueOf(i);
        item.Value = String.valueOf(i);
        items.put(item.Text, item);
    }
    
    // targetItem will be null when no item exists with the given Text value
    SelectItem targetItem = items.get('test2');
}

On the other hand, if you want to use different criteria in different scenarios you can only do this by introducing specific logic, for example:

public class SelectItem {
    public String Text { get; set; }
    public String Value { get; set; }
}

public void FooMethod() {
    List<SelectItem> itemList = new List<SelectItem>();
    for (Integer i = 0; i < 5; i++) {
        SelectItem item = new SelectItem();
        item.Text = 'test' + String.valueOf(i);
        item.Value = String.valueOf(i);
        itemList.add(item);
    }
    
    // Remember targetIndex could be -1 if there's no match
    Integer targetIndex = findIndexByText(itemList, 'test2');
    SelectItem targetItem = itemList[targetIndex];
}

private Integer findIndexByText(List<SelectItem> items, String textToMatch) {
    for (Integer index = 0; index < items.size(); index++) {
        if (items[index].Text == textToMatch) {
            return index;
        }
    }

    return -1;
}

// Other implementations on other criteria
private Integer findIndexByValue(...) { ... }

It is even possible to write a more generic mechanism, something like:

public interface Matcher {
    Boolean matches(Object item, Object value);
}

public Integer findIndex(List<Object> items, Object value, Matcher matcher) {
    for (Integer index = 0; index < items.size(); index++) {
        if (matcher.matches(items[index], value) {
            return index;
        }
    }

    return -1;
}

You then have to provide implementations of the Matcher interface though, so perhaps:

public class SelectItem {
    public String Text { get; set; }
    public String Value { get; set; }
}

public void FooMethod() {
    List<SelectItem> itemList = new List<SelectItem>();
    for (Integer i = 0; i < 5; i++) {
        SelectItem item = new SelectItem();
        item.Text = 'test' + String.valueOf(i);
        item.Value = String.valueOf(i);
        itemList.add(item);
    }
    
    // Again targetIndex could be -1 where there is no match
    Integer targetIndex = findIndex(itemList, new TextMatcher(), 'test2');
    SelectItem targetItem = itemList[targetIndex];
}

private class TextMatcher implements Matcher {
    public Boolean matches(Object item, Object value) {
        return item instanceof SelectItem && value instanceof String &&
            ((SelectItem) item).Text == ((String) value);
    }
}

That said, the latter solution is complete overkill in general terms since it adds so much code just to lift the looping and index tracking into something generic.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.