I want to write a method that takes as an input a String
key
separated by white spaces and a list of String
in which matches have to be searched.
The format of the string key
could be like this:
"s1 or s2 s3 or s4"
It has to be translated to a condition "(s1 or
s2) and
(s3 or
s4)", where conditions grouped with or
are always executed first.
The method should return a list of strings which match the condition obtained from the specified the "key".
public List<String> search(String key, ArrayList<String> s){
...
}
Example of the input (a key
and a list):
// if ((s.contains("i") || s.contains("you")) && (s.contains("boy") || s.contains("girl")) then store in the List and return finally
String key = "i or you boy or girl";
ArrayList<String> s = new ArrayList<String>(
Arrays.asList("hello world",
"i am a boy",
"i am a girl"));
Then the resulting list would contain string:
`"i am a boy"` and `"i am a girl"`.
CodePudding user response:
It can be done with predicates (function represented by a boolean condition) and regular expressions.
The overall idea is to generator a Predicate
based on string key
and then to test list elements against this predicate.
The process of parsing a Predicate
is done in following steps:
- Split the key into groups, that has to be combined afterwards with logical
AND
, based on following regular expression"(?<!or)\\s (?!or)"
(?<!or)
- negative lookbehind, represents group of characters before the matching string that must not be equal to"or"
;(?!or)
- negative lookahead, represents group of characters after the matching string that must not be equal to"or"
;\\s
- matches a string comprised from one or more white spaces.
- Split each group of strings obtained at the previous step on the
" or "
and combine the obtained predicates with logicalOR
. - Combine predicates generated at previous step into a single predicate.
(for information on regular expressions take a look at this tutorial)
Then we need to iterate over the given list and apply method test()
of the combined predicate to every element. Elements that meet with the predicate will be added into the resulting list.
public static void main(String[] args) {
String key = "i or you boy or girl";
List<String> source =
List.of("hello world", "i am a boy", "i am a girl");
Predicate<String> predicate = getPredicate(key);
List<String> result = new ArrayList<>();
for (String next: source) {
if (predicate.test(next)) {
result.add(next);
}
}
System.out.println(result);
}
public static Predicate<String> getPredicate(String key) {
Predicate<String> result = str -> true;
for (String next: key.split("(?<!or)\\s (?!or)")) {
Predicate<String> pred = str -> false;
for (String target: next.strip().split(" or ")) {
if (target.isEmpty()) {
continue;
}
pred = pred.or(str -> str.contains(target));
}
result = result.and(pred);
}
return result;
}
Output
[i am a boy, i am a girl]
Note:
- A base for combining predicates with or (
||
) has to yieldfalse
because only in this case it'll not affect the result (with (||
) only one condition can betrue
and the overall result will be evaluated totrue
). - A base for combining predicates with and (
&&
) has to returntrue
because only so the result will not get affected (with&&
all the conditions needs to be evaluated totrue
for the overall result to betrue
).