【问题标题】:ruby searching array for keywordsruby 搜索关键字的数组
【发布时间】:2012-06-04 10:10:24
【问题描述】:

我正在用 ruby​​ 脚本解析一个大型 CSV 文件,需要从一些搜索键中找到最接近的标题匹配项。搜索键可能是一个或多个值,并且这些值可能不完全匹配,如下所示(应该接近)

search_keys = ["big", "bear"]

包含我需要搜索的数据的大型数组,只想在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"]
        ]

在这种情况下,我希望它返回行 ["3", "three big bears", "2626", "a heart warmer"],因为这是与我的搜索键最接近的匹配项。

我希望它从给定的搜索键中返回最接近的匹配项。

我可以使用任何助手/库/宝石吗?以前有人这样做过吗??

【问题讨论】:

  • 您确定匹配的指标是什么?
  • 我在想标题字符串 .including? 关键字,递归所有关键字,然后得到最高命中行或类似的东西

标签: ruby arrays search


【解决方案1】:

我认为您可以自己完成,无需使用任何宝石! 这可能接近您的需要;在数组中搜索键并为每个找到的元素设置一个排名。

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

这段代码可以写得比上面的更好,但我想告诉你细节。

【讨论】:

  • 这类似于下面Isotope提供的答案,但是有一个排名系统,我喜欢这个并且认为我可能会使用它。谢谢。
  • 我将以下代码添加到您的末尾以按排名排序。 result.sort!{ |a, b| b[1] &lt;=&gt; a[1] }
【解决方案2】:

我很担心,这个任务应该由数据库级别或类似级别的任何搜索引擎处理,没有必要在应用程序中获取数据并跨列/行进行搜索等,应该很昂贵。但现在这里是简单的方法:)

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 :)

【讨论】:

    【解决方案3】:

    这行得通。将查找并返回匹配的*行数组作为result

    *matched rows = id、标题、代码或描述与提供的任何 seach_keys 匹配的行。包括部分搜索,例如“熊”中的“熊”

    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!
    

    【讨论】:

    • 我得到了这个工作,它非常简洁。如果我能得到 result.uniq!对它们进行排序,使重复次数最高的排在第一位,这样就完美了。
    【解决方案4】:

    你可以用更简洁的方式来写它......

    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"]
    

    【讨论】:

      【解决方案5】:

      这是我的单线镜头

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

      按照匹配数的顺序获取所有行

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

      任何人都知道如何组合最后一个解决方案,以便删除不包含任何键的行并保持简洁?

      【讨论】:

      • 这个解决方案看起来很酷。我无法让第一行工作,但我可以让第二行删除所有结果而没有命中,这将非常有用。
      • 很高兴听到,但令我惊讶的是其中一个不起作用,你使用 Ruby193,那么它们应该同时工作,第一个给出多维数组的过滤版本,第二个是排序的版本减去标题行
      猜你喜欢
      • 1970-01-01
      • 2019-04-16
      • 2022-01-21
      • 2018-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多