SteveSt
SteveSt

Reputation: 1297

Dynamically filter List with Predicate

I have the following list of Strings:

{"New York","London","Paris","Berlin","New York"}

I am trying to use the Guava Library and I want to filter this list in such a way that I will get only the strings which will equal to a string that I will provide. If I had a fixed value let's say "New York" I would do the following:

Predicate<String> myCity = new Predicate<String>() {
    @Override public boolean apply(String city) {
        return city == "New York";
    }               
};

But what if I want the return statement to be something like this :

return city == myStringVariable

Can I give the argument for the city to the Predicate or combine two predicates somehow ?

Upvotes: 1

Views: 7318

Answers (3)

Frank Pavageau
Frank Pavageau

Reputation: 11705

Guava provides a number of really generic Predicates, via the Predicates helper class.

To filter on equality (be it for a String or any other object), it provides the equalTo() predicate:

Predicate<String> myCity = Predicates.equalTo(myStringVariable);

Update to answer a question in the comments: how to filter when the list is not of Strings but of objects which have a String property.

You have several options, depending on what you already have:

  1. Use imperative code

    For a one-time use, unless you use Java 8, it's a bit verbose to use the functional constructs. See the FunctionalExplained page in the wiki.

    private List<SomeObject> filterOnMyCity(List<SomeObject> list,
                                            String value) {
        List<SomeObject> result = new ArrayList<>();
        for (SomeObject o : list) {
            if (value.equals(o.getMyCity())) {
                result.add(o);
            }
        }
        return result;
    }
    
  2. Use an ad-hoc predicate

    class MyCityPredicate implements Predicate<SomeObject> {
         private final String value;
    
         public MyCityPredicate(String value) {
             this.value = value;
         }
    
         @Override
         public boolean apply(@Nullable SomeObject input) {
             return input != null && value.equals(input.getPropertyX());
         }
    }
    
    return FluentIterable.from(list)
            .filter(new MyCityPredicate(myStringVariable))
            .toList();
    
  3. Use an existing Function

    class MyCityFunction implements Function<SomeObject, String> {
         @Override
         public String apply(@Nullable SomeObject input) {
             return input == null ? null : input.getPropertyX();
         }
    }
    
    return FluentIterable.from(list)
            .filter(Predicates.compose(
                    Predicates.equalTo(myStringVariable),
                    new MyCityFunction()))
            .toList();
    

However, I wouldn't override equals() as mentioned in the comments, it could be too specific to say that 2 instances of SomeObject are equals just because one of their properties is. It doesn't scale if you need to filter on 2 different properties in different contexts.

If you have Java 8, the code becomes much more compact using lambda expressions, and you can directly use the Stream API anyway so you don't need Guava for that.

Upvotes: 8

NeeL
NeeL

Reputation: 720

Either you use a final String or subclass of Predicate :

final String testStr = textbox.getText(); //for example
Predicate<String> myCity = new Predicate<String>() {
    @Override public boolean apply(String city) {
        return city.equals(testStr);
    }               
};

or

public class CityPredicate implements Predicate<String>{
    String cityName;
    public CityPredicate(String cityName){
        this.cityName = cityName;
    }
    @Override public boolean apply(String city) {
        return city.equals(cityName);
    } 
}
//Use example :
Predicate<String> myCity = new CityPredicate("New York");

And as @Sotirios Delimanolis told you, always compare String with equals()

EDIT : example with Frank Pavageau's solution : Given your class :

public class City{
    String cityName;
    public City(String cityName){
        this.cityName=cityName;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof String){
            return cityName.equals(obj);
        }
        return false;
    }
}

List<City> cities =  Lists.newArrayList(new City("New York"),new City("Chicago"));
Predicate<String> myCityPredicate = Predicates.equalTo("New York");
final List<City> res = Lists.newArrayList(Iterables.filter(cities , myCityPredicate));
//res.size() will be 1 and containing only your City("New York")
//To check whether it is present you can do :
final boolean isIn = Predicates.in(cities).apply("New York");

Upvotes: 5

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279950

You can construct the predicate by closing over a some other variable

final String cityName = "New York";
Predicate<String> myCity = new Predicate<String>() {
    @Override public boolean apply(String city) {
        return city.equals(cityName);
    }               
};

Note how I compare strings with their equals method rather than the == reference equality operator. See below for why.

How do I compare strings in Java?

Upvotes: 2

Related Questions