2012-05-30 22 views
5

Estoy analizando un archivo CSV grande en un script de ruby ​​y necesito encontrar la coincidencia más cercana para un título de algunas claves de búsqueda. Las claves de búsqueda tal vez uno o más valores y los valores pueden no coincidir exactamente según abajo (debería estar cerca)matriz de búsqueda ruby ​​para palabras clave

search_keys = ["big", "bear"] 

Una gran matriz que contiene los datos que tengo que buscar a través, sólo desea buscar en la columna de la title :

array = [ 
      ["id", "title",   "code", "description"], 
      ["1", "once upon a time", "3241", "a classic story"], 
      ["2", "a big bad wolf", "4235", "a little scary"], 
      ["3", "three big bears", "2626", "a heart warmer"] 
     ] 

En este caso me gustaría que vuelva la fila ["3", "three big bears", "2626", "a heart warmer"] ya que este es el valor más cercano a mis claves de búsqueda.

Quiero que devuelva la coincidencia más cercana de las claves de búsqueda dadas.

¿Hay ayudantes/bibliotecas/gemas que pueda usar? ¿Alguien hizo esto antes?

+0

¿Cuál es su métrica para determinar una coincidencia? – alex

+0

Estaba pensando en la cadena de título '.including?' La palabra clave, recursivamente para todas las palabras clave y luego obtengo la fila de hit más alta o algo así – Norto23

Respuesta

1

¡Creo que puede hacerlo solo y sin necesidad de utilizar gemas! Esto puede estar cerca de lo que necesita; buscando en el arreglo las claves y estableciendo un rango para cada elemento encontrado.

result = [] 
array.each do |ar| 
    rank = 0 
    search_keys.each do |key| 
     if ar[1].include?(key) 
      rank += 1 
     end 
    end 

    if rank > 0 
     result << [rank, ar] 
    end 
end 

Este código se puede escribir mejor que el anterior, pero quería mostrarle los detalles.

+0

Esto es similar a la respuesta proporcionada por Isotopo a continuación, pero con un sistema de clasificación, me gusta este y creo que podría usarlo. Gracias. – Norto23

+1

Agregué el siguiente código al final del suyo para ordenar por rango. 'result.sort! {| a, b | b [1] <=> a [1]} ' – Norto23

2

Me preocupa que esta tarea deba manejarse en cualquier motor de búsqueda a nivel db o similar, ningún punto de búsqueda de datos en la aplicación y hacer búsquedas entre columnas/filas, etc., debería ser costoso. pero por ahora aquí está el enfoque sencillo simple :)

array = [ 
      ["id", "title",   "code", "description"], 
      ["1", "once upon a time", "3241", "a classic story"], 
      ["2", "a big bad wolf", "4235", "a little scary"], 
      ["3", "three big bears", "2626", "a heart warmer"] 
     ] 


h = {} 

search_keys = ["big", "bear"] 

array[1..-1].each do |rec| 
    rec_id = rec[0].to_i 

    search_keys.each do |key| 
    if rec[1].include? key 
     h[rec_id] = h[rec_id] ? (h[rec_id]+1) : 1 
    end 
    end 
end 

closest = h.keys.first 

h.each do |rec, count| 
    closest = rec if h[closest] < h[rec] 
end 

array[closest] # => desired output :) 
1

Esto funciona. Buscará y devolverá una matriz de filas coincidentes * como result.

* filas coincidentes = una fila donde la identificación, el título, el código o la descripción coinciden con CUALQUIERA de las claves_seach provistas. incl búsquedas parciales como 'oso' en 'osos'

result = [] 
array.each do |a| 
    a.each do |i| 
     search_keys.each do |k| 
      result << a if i.include?(k) 
     end 
    end 
end 
result.uniq! 
+0

Tengo este para trabajar, es muy breve. Si pudiera obtener el resultado.uniq! para ordenarlos de modo que los duplicados más altos sean los primeros, entonces esto sería perfecto. – Norto23

1

probablemente Se puede escribir de una manera más sucinta ...

array = [ 
      ["id", "title",   "code", "description"], 
      ["1", "once upon a time", "3241", "a classic story"], 
      ["2", "a big bad wolf", "4235", "a little scary"], 
      ["3", "three big bears", "2626", "a heart warmer"] 
     ] 
search_keys = ["big", "bear"] 


def sift(records, target_field, search_keys) 
    # find target_field index 
    target_field_index = nil 
    records.first.each_with_index do |e, i| 
     if e == target_field 
      target_field_index = i 
      break 
     end 
    end 
    if target_field_index.nil? 
     raise "Target field was not found" 
    end 

    # sums up which records have a match and how many keys they match 
    # key => val = record => number of keys matched 
    counter = Hash.new(0) # each new hash key is init'd with value of 0 

    records.each do |record| # look at all our given records 
     search_keys.each do |key| # check each search key on the field 
      if record[target_field_index].include?(key) 
       counter[record] += 1 # found a key, init to 0 if required and increment count 
      end 
     end 
    end 

    # find the result with the most search key matches 
    top_result = counter.to_a.reduce do |top, record| 
     if record[1] > top[1] # [0] = record, [1] = key hit count 
      top = record # set to new top 
     end 
     top # continue with reduce 
    end.first # only care about the record (not the key hit count) 
end 


puts "Top result: #{sift array, 'title', search_keys}" 
# => Top result: ["3", "three big bears", "2626", "a heart warmer"] 
1

Aquí está mi una línea disparó

p array.find_all {|a|a.join.scan(/#{search_keys.join("|")}/).length==search_keys.length} 
=>[["3", "three big bears", "2626", "a heart warmer"]] 

para obtener todas las filas en orden de número de coincidencias

p array.drop(1).sort_by {|a|a.join.scan(/#{search_keys.join("|")}/).length}.reverse 

¿Alguien sabe cómo combinar la última solución para que las filas que no contienen ninguna de las claves se eliminen y mantenerla conciso como está?

+0

esta solución se ve muy bien.No pude hacer funcionar la primera línea, pero podría obtener la segunda línea para soltar todos los resultados sin hits, sería muy útil. – Norto23

+0

es agradable de escuchar, pero me sorprende que uno de ellos no funcione, ¿usas Ruby193, entonces deberían funcionar ambos, el primero da una versión filtrada de tu matriz multidimensional, el segundo una versión ordenada menos el headerrow – peter

Cuestiones relacionadas