# Better way of searching for values in apex list

I have a list of unique strings say 'one' to 'ten' as below:

List<String> values = {'one', 'two', 'three','four','five','six','seven','eight','nine','ten'};

I have a requirement to check if the list consists of below:

Return true if list consists of either 'one' or 'two' or 'three' or all of them but not others(for eg: 'five' or 'six')

i.e.

Return true if list consists of 'one'
Return true if list consists of 'one', 'two'
Return true if list consists of 'one', 'three'
Return true if list consists of 'one', 'two', 'three'
Return true if list consists of 'two'
Return true if list consists of 'two', 'three'
Return true if list consists of 'three'

In all other cases, return false. For eg:

Return false if list consists of 'one', 'four'
Return false if list consists of 'six', 'four', 'nine'
Return false if list consists of 'one, 'two', 'three, 'four', 'six', 'nine

I am ending up with too many if statments in my code. Is there a better way of achieving this logic?

Lists are not a great match for this problem. What you should be looking at instead is a Set.

The Set class/type provides some useful methods, containsAll(), retainAll(), removeAll()

Thinking in terms of those methods, if you remove 'one', 'two', and 'three' and your set isn't empty, then you have at least one of 'four' through 'ten'. That's enough to get you a reliable true/false determination, and in fewer lines.

ex.

Set<String> myTestSet; // initialized elsewhere, perhaps passed into a method
Set<String> allowedSet = new Set<String>{'one', 'two', 'three'};

myTestSet.removeAll(allowedSet);

Boolean result = myTestSet.isEmpty();

You can use Set methods to efficiently determine if your condition is met:

public static Boolean containsOnly(List<String> valuesToCheck, List<String> validValues) {
Set<String> valueSetToCheck = new Set<String>(valuesToCheck);
Set<String> valueSetValid = new Set<String>(validValues);
valueSetToCheck.removeAll(valueSetValid);
return valueSetToCheck.isEmpty();
}

This is an unoptimized version; you can create the valueSetValid Set just once, and use it with removeAll over and over again. By removing the valid values, the Set will be empty if there were no values outside our desired range. Any values left in the Set after removeAll indicates that other values were present.

// Demo
List<Set<String>> values = new List<Set<String>> {
new Set<String> { 'one'},
new Set<String> { 'one', 'two' },
new Set<String> { 'one', 'three' },
new Set<String> { 'one', 'two', 'three' },
new Set<String> { 'two' },
new Set<String> { 'two', 'three' },
new Set<String> { 'three' },
new Set<String> { 'one', 'four' },
new Set<String> { 'six', 'four', 'nine' },
new Set<String> { 'one', 'two', 'three', 'four', 'six', 'nine' }
};
Set<String> validValues = new Set<String>{'one','two','three'};
for(Set<String> testValues: values) {
Set<String> dupValues = testValues.clone();
dupValues.removeAll(validValues);
System.debug('Return '+dupValues.isEmpty()+' for '+JSON.serialize(testValues));
}

// Output
DEBUG|Return true for ["one"]
DEBUG|Return true for ["one","two"]
DEBUG|Return true for ["one","three"]
DEBUG|Return true for ["one","two","three"]
DEBUG|Return true for ["two"]
DEBUG|Return true for ["two","three"]
DEBUG|Return true for ["three"]
DEBUG|Return false for ["one","four"]
DEBUG|Return false for ["six","four","nine"]
DEBUG|Return false for ["one","two","three","four","six","nine"]