Aquí hay un código que funciona, pero parece poco elegante. ¿Cuál es una mejor manera de buscar cualquier aparición de estas cadenas dentro de otra cadena?

String AndyDaltonInjury = "broken right thumb";

if (AndyDaltonInjury.toLowerCase().contains("broken") &&
    (AndyDaltonInjury.toLowerCase().contains("knee") ||
    AndyDaltonInjury.toLowerCase().contains("leg")   ||
    AndyDaltonInjury.toLowerCase().contains("ankle") ||
    AndyDaltonInjury.toLowerCase().contains("thumb") ||
    AndyDaltonInjury.toLowerCase().contains("wrist"))) 
{
    System.out.println("Marvin sends in the backup quarterback.");  
}
3
Caleigh O'Brien 13 sep. 2018 a las 16:06

5 respuestas

La mejor respuesta

Use la colección Set y su método {{ X1}} insde transmitiendo la matriz dividida con el delimitador de espacio (" "):

Set<String> set = new HashSet<>(Arrays.asList("knee", "leg", "ankle", "thumb", "wrist"));

String lower = "broken right thumb".toLowerCase();
String split[] = lower.split(" ");
if (lower.contains("broken") && Arrays.stream(split).anyMatch(set::contains)) {
    System.out.println("Marvin sends in the backup quarterback.");
}

Además, le recomiendo que use nombres de variables en minúsculas.

8
Nikolas 13 sep. 2018 a las 13:16

Como alternativa a una solución basada en Set ya publicada (que por cierto me parece mejor, en el sentido de legibilidad), esto se puede hacer usando una expresión regular:

final Pattern brokeStuffPattern = Pattern.compile(
    ".*\\bbroken?\\b.*\\b(?:knee|leg|ankle|thumb|wrist)s?\\b.*"
    + "|.*\\b(?:knee|leg|ankle|thumb|wrist)s?\\b.*\\bbroken?\\b.*",
    Pattern.CASE_INSENSITIVE
);
if (brokeStuffPattern.matcher(AndyDaltonInjury).matches()) {
    ...
}

Esto explicaría los plurales y el tiempo perfecto del verbo también, p. si coincidiría con "piernas rotas".

1
Alex Shesterov 13 sep. 2018 a las 13:19

Es probable que los algoritmos basados en hash le brinden un mejor rendimiento si necesita verificar una gran cantidad de texto en busca de ocurrencias dentro de un conjunto enorme.

HashSet sería un buen primer intento ya que la búsqueda (prueba si la clave contenida dentro del conjunto) se realizará entre O (1) y O (n).

Sin embargo, le recomiendo encarecidamente que busque el beneficio de emplear un [Bloom Filter][1]. Servirá bien como prefiltro ya que proporciona un rendimiento predecible de O (k). Debido a que el filtro tendrá un pequeño porcentaje de falsos positivos, también deberá ejecutar una segunda etapa.

Busque Guava BloomFilter para una buena implementación.

Otro beneficio del filtro Bloom es que no contiene el conjunto de datos original, solo un hash reducido, lo que significa que su tamaño es mínimo. Esto significa que es más adecuado para sistemas distribuidos, ya que se copia de manera muy eficiente. En un entorno como Apache Spark, incluso configuraría esto como una variable de difusión, ya que una vez producida suele ser constante en el tiempo.

0
YoYo 16 sep. 2018 a las 00:44

Puede crear las funciones que faltan (contiene todas / cualquiera) como métodos, o expresarlas usando anotaciones de Lambda:

BiPredicate<String, List<String>> containsAll = (text, words) -> 
    words.stream().allMatch(word -> text.toLowerCase().contains(word));
BiPredicate<String, List<String>> containsAny = (text, words) -> 
    words.stream().anyMatch(word -> text.toLowerCase().contains(word));

if (containsAll.test(AndyDaltonInjury, Arrays.asList("broken")) && 
    containsAny.test(AndyDaltonInjury, Arrays.asList("knee", "leg", "ankle", "thumb", "wrist"))) {
    System.out.println("Marvin sends in the backup quarterback.");
}
0
Peter Walser 13 sep. 2018 a las 13:16

Puedes probar esto:

String test = "broken right thumb";

Predicate << ? super String > machCriteria = s - > Stream.of("knee", "leg", "ankle", "thumb", "wrist").anyMatch(e - > e.equals(s.toLowerCase()));
String result = Pattern.compile(" ").splitAsStream(test).anyMatch(machCriteria) ? "Marvin sends in the backup quarterback." : "";
System.out.println(result);
0
MohammadReza Alagheband 13 sep. 2018 a las 13:27