Data filtering in java

jlacar / Main.java

This example shows how you can create a flexible way to filter a set of data. The program allows you to specify different conditions and ways to apply them to the data. The design uses Predicates, lambdas, and enums to implement the Strategy Pattern.

The program has a list of Person objects that can be filtered in different ways. One way is to filter by age range. Another is by partial name. Any number of filters can be defined. The filters can be applied to the data in two ways: 1) accept items that match any of the criteria and 2) accept items that match all of the criteria.

Suppose the program starts with the following list of Person objects

name=Bob Burgess, age=34 name=Cathy Burgess, age=29 name=Michael Johns, age=15 name=Kim Johnwell, age=27 name=Mary Stevens, age=37 name=Kimberly Johnson, age=21 

Then an age range filter is defined:

Enter age range (low high): 12 22 Filter added: Person.age in [12..22] 

Since there is only filter defined, both the ANY and ALL strategies filter the list the same way:

name=Michael Johns, age=15 name=Kimberly Johnson, age=21 

However, if another filter is added:

Enter name (or part of it): Kim Filter added: Person.name contains "Kim" 

then the list where ANY of the filters match would be

name=Michael Johns, age=15 name=Kim Johnwell, age=27 name=Kimberly Johnson, age=21 

whereas the list where ALL of the filters match would be

name=Kimberly Johnson, age=21 

The following sections explain the key parts of the design.

The data class on which the filtering will be applied. For simplicity, the name and age attributes are made accessible.

The values of this enum define the different strategies for composing the predicates that will be used to filter the data set. Each enum value provides its own implementation of the abstract accumulator() method. The return type is a BinaryOperator> that is used in the Stream.reduce() operation invoked in the Main.matches() method.

The ANY enum value uses Predicate.or() to compose all the filters defined by the user.

The ALL enum value uses Predicate.and() to compose all the filters defined by the user.

Prints out all the matching Person objects that meet the criteria applied per the given MatchStrategy .

Returns a composed Predicate based on the given MatchStrategy . It uses the strategy’s accumulator() to combine all the user-defined filters into a single composed predicate. For example, ANY provides an accumulator that will compose/chain all the user-defined filters using the Predicate.or() method.

The linchpin to all this is the .reduce(strategy.accumulator()) call. This step in the stream processing produces an Optional> , hence the call to .orElse() at the end to return a concrete Predicate . The argument to .orElse() is noFilter -> true which tries to convey the net effect of having no filter since the predicate always returns true . The name was intentionally chosen to read like a noFilter flag is being set to true .

A Static Factory class that provides different methods for producing a Predicate that captures user-defined parameters. This gives the user the flexibility to define different «customized» filters based on specific values they provide. To add more types of criteria, just add another factory method that encapsulates the predicate logic in a similar fashion as the other factory methods.

Returns a Predicate to filter People based on their ages falling within a certain range. The user enters the low and high boundaries of the desired range. When applied, the filter will accept Person objects with age that falls in the given range (inclusive of both boundaries).

The returned closed lambda captures the low and high values entered by the user.

Returns a Predicate to filter People based on their names. The user enters a name or part of a name. When applied, the filter will accept Person objects in which the entered string appears anywhere in Person.name .

The returned closed lambda captures the partialName value entered by the user.

1 — What is the difference between a «closure» and a «lambda»: You keep saying «closure» but I don’t think it means what you probably think it means. See the answer by SasQ at https://stackoverflow.com/questions/220658/what-is-the-difference-between-a-closure-and-a-lambda

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters

import java . util .*;
import java . util . function .*;
/**
* Sample program to show use of Predicates and composing Predicates,
* capturing variables in lambdas expressions, and using enums to
* implement the Strategy pattern.
*
* Inspired by this discussion on CodeRanch.com:
* https://coderanch.com/t/729390/engineering/Design-pattern-good-command-pattern
*/
class Main
private static Scanner input = new Scanner ( System . in );
private List < Predicate < Person >> filters = new ArrayList <>();
private List < String >filterDescriptions = new ArrayList <>();
private List < Person >people = new ArrayList <>();
people . addAll ( Arrays . asList (
new Person ( «Bob Burgess» , 34 ),
new Person ( «Cathy Burgess» , 29 ),
new Person ( «Michael Johns» , 15 ),
new Person ( «Liz Carter» , 12 ),
new Person ( «Bob Jameson» , 47 ),
new Person ( «Jim Stevens» , 24 ),
new Person ( «Edgar Burris» , 58 ),
new Person ( «Rudolfo Kimmick» , 57 ),
new Person ( «Carter Johns» , 37 ),
new Person ( «Jason Mark Carter» , 72 ),
new Person ( «Burton Stevenson» , 33 ),
new Person ( «Tom Church» , 58 ),
new Person ( «Vladimir Oliva» , 52 ),
new Person ( «Cathy Burton» , 42 ),
new Person ( «Kim Johnwell» , 27 ),
new Person ( «Mary Stevens» , 37 ),
new Person ( «Kimberly Johnson» , 21 )
));
>
public static void main ( String [] args )
new Main (). runDemo ();
>
void runDemo ()
int action = 0 ;
do
action = getMenuChoice ();
switch ( action )
case 1 :
case 2 :
case 3 :
case 4 :
case 5 :
case 6 :
case 7 :
case 8 :
>
> while ( action != 0 );
>
int getMenuChoice ()
System . out . print ( » \n Menu: \n » +
«1 — Add age range filter \n » +
«2 — Add name filter \n » +
«3 — Show matches ANY criteria \n » +
«4 — Show matches ALL criteria \n » +
«5 — Show all filters \n » +
«6 — Clear filters \n » +
«7 — Show all people \n » +
«8 — Add people \n » +
«0 — Exit \n » +
«Choice: » );
int choice = input . nextInt ();
input . nextLine ();
return choice ;
>
void showList ( MatchStrategy strategy )
if ( filters . isEmpty ())
showFullList ();
> else
System . out . printf ( «People who meet %s of the criteria:%n» , strategy );
people . stream ()
. filter ( matches ( strategy ))
. forEach ( System . out :: println );
>
>
void showFullList ()
System . out . println ( «All the people in the list:» );
people . stream (). forEach ( System . out :: println );
>
void showFilters ()
if ( filterDescriptions . isEmpty ())
System . out . println ( «No filters defined.» );
> else
System . out . println ( «Filters defined:» );
filterDescriptions . stream (). forEach ( System . out :: println );
>
>
void addAgeFilter ()
System . out . print ( «Enter age range (low high): » );
int low = input . nextInt ();
int high = input . nextInt ();
input . nextLine ();
filters . add ( PersonFilter . byAgeRange ( low , high ));
addFilterDescription ( String . format ( «Person.age in [%d..%d]» , low , high ));
>
void addNameFilter ()
System . out . print ( «Enter name (or part of it): » );
String partialName = input . nextLine (). trim ();
filters . add ( PersonFilter . nameContains ( partialName ));
addFilterDescription ( String . format ( «Person.name contains \» %s \» » , partialName ));
>
Predicate < Person >matches ( MatchStrategy strategy )
return filters . stream ()
. reduce ( strategy . accumulator ())
. orElse ( noFilter -> true );
>
void addFilterDescription ( String description )
System . out . printf ( «Filter added: %s%n» , description );
filterDescriptions . add ( description );
>
void clearFilters ()
filters . clear ();
filterDescriptions . clear ();
System . out . println ( «All filters cleared.» );
>
void addPeople ()
System . out . println ( «Enter ‘DONE’ to go back:» );
do
System . out . print ( «# : » );
String entry = input . nextLine ();
if ( entry . startsWith ( «DONE» )) break ;
String [] parts = entry . trim (). split ( «#» );
addPerson ( parts [ 0 ], Integer . valueOf ( parts [ 1 ]));
> while ( true );
showFullList ();
>
void addPerson ( String name , int age )
people . add ( new Person ( name , age ));
System . out . printf ( «Added person: %s%n» , people . get ( people . size ()- 1 ));
>
>
enum MatchStrategy
ANY
@ Override
BinaryOperator < Predicate < Person >> accumulator ()
return ( composed , criteria ) -> composed . or ( criteria );
>
>,
ALL
@ Override
BinaryOperator < Predicate < Person >> accumulator ()
return ( composed , criteria ) -> composed . and ( criteria );
>
>;
abstract BinaryOperator < Predicate < Person >> accumulator ();
>;
class Person
String name ;
int age ;
Person ( String name , int age )
this . name = name ;
this . age = age ;
>
@ Override
public String toString ()
return String . format ( «name=%s, age=%d» , name , age );
>
>
/* Static Factory for Predicate */
class PersonFilter
private PersonFilter () <>
/* NOTE: Captured parameters should be declared final */
static Predicate < Person >byAgeRange ( final int low , final int high )
// lambda is closed by capturing low and high (its «closure»)
return person -> low
>
static Predicate < Person >nameContains ( final String partialName )
// lambda is closed by capturing partialName (its «closure»)
return person -> person . name . contains ( partialName );
>
>

Источник

Читайте также:  Изменить размер контейнера css
Оцените статью