Reputation: 59
I'm given an unordered list of objects of this form
class Element {
String property; // has value from the set ("A", "B", "C", "D")
}
where no two elements have the same property
value.
I want to choose one element according to a set of rules like the following and return its property
value:
property
has value "A"
, then pick that element.property
has value "B"
, then pick that element.property
has value "C"
, then pick that element.This is one solution I've come up with for this problem:
String chooseElementProperty(List<Element> elements) {
// Iterates the list and creates a set of all the property values.
// example: [{property: 'A'}, {property: 'B'}] -> {'A', 'B'}
Set<String> propertyValues = pluckPropertyValues(elements);
if (propertyValues.contains("A"))
return "A";
if (propertyValues.contains("B"))
return "B";
if (propertyValues.contains("C"))
return "C";
throw new IllegalArgumentException("Invalid input!");
}
My question is: What is a better/cleaner way to do this that makes this method more extensible where it's easier to change my preference if in the future I want to choose "B"
over "A"
?
Upvotes: 4
Views: 1225
Reputation: 54659
The current answers seem to assume that the order is "A", "B", "C"
, and thus, may have overlooked the last, crucial statement:
What is a better/cleaner way to do this that makes this method more extensible where it's easier to change my preference if in the future I want to choose
"B"
over"A"
?
Beyond that, the solutions may be considered as ... "tricky" ... or complex, from a computational point of view. I'd vote for a solution that is similar to yours, but without the creation of the list of properties, and basically just puts the preferences into a list to iterate over it:
import java.util.Arrays;
import java.util.List;
class Element
{
String property;
Element(String property)
{
this.property = property;
}
}
public class PickByPreference
{
public static void main(String[] args)
{
List<Element> elements = Arrays.asList(
new Element("B"),
new Element("C"),
new Element(null),
new Element("A"));
System.out.println(chooseElementProperty(elements));
}
static String chooseElementProperty(List<Element> elements)
{
// Change this to change the preferences
// (or pass it in as a parameter)
List<String> preferences = Arrays.asList("A", "B", "C");
for (String preference : preferences)
{
for (Element element : elements)
{
if (preference.equals(element.property))
{
return element.property;
}
}
}
throw new IllegalArgumentException("Invalid input!");
}
}
Edit: In terms of asymptotic complexity, extracting the properties into a Set
may be beneficial, though, particularly if you have many elements. In this case, the implementation would be
static String chooseElementProperty(List<Element> elements)
{
// Change this to change the preferences
// (or pass it in as a parameter)
List<String> preferences = Arrays.asList("A", "B", "C");
Set<String> propertyValues = pluckPropertyValues(elements);
for (String preference : preferences)
{
if (propertyValues.contains(preference))
{
return element.property;
}
}
throw new IllegalArgumentException("Invalid input!");
}
Upvotes: 2
Reputation: 18933
The previous answers are correct.
My two cents would be to avoid contains()
as it uses indexOf()
So use
if (propertyValues.indexOf("A") > -1)
return "A";
if (propertyValues.indexOf("B") > -1)
return "B";
if (propertyValues.indexOf("C") > -1)
return "C";
Upvotes: 1
Reputation: 425198
Try this:
List<String> targets = Arrays.asList("A", "B", "C");
return elements.stream()
.map(this::pluckPropertyValues)
.flatMap(Set::stream)
.filter(targets::contains)
.sorted(Comparator.comparing(targets::indexOf))
.findFirst()
.orElseThrow(IllegalArgumentException::new);
This will work for an arbitrary list of targets, with preferences based on their position in the list.
Disclaimer: Code may not compile or work as it was thumbed in on my phone (but there's a reasonable chance it will work)
Upvotes: 2
Reputation: 4592
Rather than having the desired property values hard-coded into chooseElementProperty
, provide them in an additional argument:
String chooseElementProperty(List<Element> elements, List<String> prefs) {
// Iterates the list and creates a set of all the property values.
// example: [{property: 'A'}, {property: 'B'}] -> {'A', 'B'}
Set<String> propertyValues = pluckPropertyValues(elements);
for (String s: prefs) {
if (propertyValues.contains(s))
return s;
}
throw new IllegalArgumentException("Invalid input!");
}
Here I've used a List<String>
, but you could also use a String[]
or even a String...
varargs if you prefer.
Upvotes: 4
Reputation: 5613
Assuming all the property values are letters, you can sort the list and provide a comparator
to sort by propery
then validate the property of the first item
Collections.sort(elements, new Comparator<Element>() {
@Override
public int compare(Element a, Element b) {
return a.property.compareTo(b.property);
}
});
List<String> check = Arrays.asList("A", "B", "C");
String tmp = elements.get(0).property;
if check.contains(tmp)
return tmp
else
throw new IllegalArgumentException("Invalid input!");
You can also sort using lambda:
Collections.sort(elements, (e1, e1) -> e1.property.compareTo(e2.property));
Upvotes: 3