2010-01-21 7 views
5

Tengo una variedad de nombres de calles ordenados alfabéticamente que he recopilado de un servicio web. Esta matriz existe en el lado del servidor.En PHP, ¿qué es una forma rápida de buscar en una matriz los valores que contienen una subcadena?

En el lado del cliente, un usuario empieza a escribir el nombre de la calle en la que vive en y AJAX se utiliza para devolver una lista de la coincidencia más cercana a la parte del nombre de la calle, además de los próximos 9 nombres de las calles de la matriz (el la lista se actualiza mientras él está escribiendo).

Por ejemplo, si el usuario ha escrito "al", que sería de esperar que los resultados sean algo como lo siguiente:

  • Albany Hwy
  • Albens Vale
  • Alcaston Rd
  • Alex Wood Dr.
  • Alice Rd
  • Allawah Ct
  • Allen Rd
  • Alloway Pl
  • Allwood Av
  • Alola St
  • Amanda Dr

Este es mi intento en la que:

$matches = array(); 
for($i = 0; $i < count($streetNames); $i++) 
{ 
    if((stripos($streetNames, $input) === 0 && count($matches) == 0) || count($matches) < 10){ 
    $matches[] = $streetNames[$i]; 
    } else { 
    break; 
    } 
} 

¿Alguien más sabe una manera más rápida?

Tenga en cuenta: No tengo ningún control sobre cómo se obtiene esta lista de la base de datos - que es de un servicio web externo.

+0

Bueno, a averiguar el más rápido * * manera, tendría que establecer criterios de referencia a estar seguro. Pero si esto proviene de un servicio web externo, diría que construir la conexión al servicio web será más lento que cualquier código que obtendría para obtener respuestas. – Gordon

+0

Sí, lo he solucionado guardando en caché los datos devueltos del servidor web durante 24 horas. Los nombres de las calles en nuestro municipio generalmente no cambian mucho, pero hay un gran desarrollo y nuevas calles que aparecen todo el tiempo, por lo que 24 horas parece una buena cantidad de tiempo. –

Respuesta

4

La única manera de llegar más rápido que mirando a través de todas las cadenas sería tener una estructura de datos optimizada para este tipo de cosas, un trie. Es posible que no tenga control sobre lo que le ofrece el servicio web, pero si puede almacenar en caché el resultado en su servidor y reutilizarlo para atender muchas solicitudes, entonces construir un trie y usarlo sería mucho más rápido.

+0

Interesante, porque en realidad estoy almacenando en caché los datos del servidor web. Definitivamente investigaré esto :) –

+0

¡Mate, respuesta legendaria! Encontré un muy buen recurso php también: http://phpir.com/tries-and-wildcards –

4

creo que lo que estás buscando es preg_grep()

Puede buscar cualquiera de los elementos que comienzan con el texto de entrada:

$result = preg_grep('/^$input/', $streetNames); 

o para los elementos que contienen el texto en cualquier lugar:

$result = preg_grep('/$input/', $streetNames); 

o también puede anclar la búsqueda hasta el final, pero eso no parece tan útil

+0

Gracias por la respuesta, nunca había oído hablar de preg_grep. Si bien no lo usaré en este caso, parece muy útil y lo archivaré para más adelante :) –

5

Uso preg_grep():

$matches = preg_grep('/al/', $streetNames); 

Nota: este método como la suya será una búsqueda de fuerza bruta. Si está buscando una gran lista de nombres (cientos de miles) o buscando en una gran cantidad de veces, es posible que necesite algo mejor. Sin embargo, para pequeños conjuntos de datos esto está bien.

+0

Gracias cletus. Si bien no usaré este método en esta instancia en particular, me has abierto los ojos a una función que de otro modo siempre habría pasado por alto. Definitivamente usaré esto en algún lugar de la pista. Gracias de nuevo :) –

+0

Esto nunca sería una manera rápida: | – s3v3n

4

no se puede decir si es más rápido, pero esta es mi versión de la misma.

$input = 'al'; 
$matches = array_filter($streetNames, create_function('$v','return (stripos($v,'.$input.') !== false ? true : false);')); 
$weight = array_map(create_function('$v','return array($v,levenshtein('.$input.',$v));'),$matches); 
uasort($weight, create_function('$a,$b', 'if ($a[1] == $b[1]) {return 0;} return ($a[1] < $b[1]) ? -1 : 1;')); 
$weight = array_slice($weight, 0, 10); 

Esto crea una lista ponderada de los partidos. Se ordenan según la distancia entre la cadena de entrada y el nombre de la calle. 0 representa una coincidencia verdadera.

matriz resultante es la siguiente

array (
    0 => 
    array (
    0 => 'Alola St', 
    1 => 7, 
), 
    1 => 
    array (
    0 => 'Allen Rd', 
    1 => 7, 
) 
) 

donde 0 => nombre de la calle y 1 => distancia levenshtein

+0

Hola, buen trabajo me gusta tu sistema de ponderación :) –

+0

Para mí, una autocompleta no está completa sin tal ponderación o lo que quieras llamar eso. Pero, por supuesto, no es la única forma de hacerlo. Solo una prueba rápida de concepto. –

Cuestiones relacionadas