简体   繁体   English

使用ruby在多维数组中找到最接近的值

[英]Find the closest value in multidimensional arrays with ruby

I need to find the closest values from a set multidimensional arrays, here is the example: 我需要从一组多维数组中找到最接近的值,这是示例:

a = [[a,b,1,2,3],[a,b,5,6,7],[a,b,8,9,10],[c,d,1,2,9],[c,d,1,7,8]]

I want to search the value closest to 1.8 from the overall arrays that contain the same elements in the first 2 index. 我想从在前2个索引中包含相同元素的整个数组中搜索最接近1.8的值。 My expected result is this: 我的预期结果是这样的:

a = [[a,b,1,2,3],[c,d,1,2,9]]

Any ideas? 有任何想法吗? Thanks! 谢谢!

a = [[:a, :b, 1, 2, 3], [:a, :b, 5, 6, 7], [:a, :b, 8, 9, 10], [:c, :d, 1, 2, 9], [:c, :d, 1, 7, 8]]

a.
  group_by {|a, b, *_| [a, b] }.
  map {|_k, v|
    v.min_by {|_, _, *nums|
      nums.map {|num| (1.8 - num).abs }.min }}
# => [[:a, :b, 1, 2, 3], [:c, :d, 1, 2, 9]]
  1. Group the arrays by their first two elements 按数组的前两个元素分组
  2. We are not interested in the keys, so we map to v 我们对键不感兴趣,因此我们mapv
  3. We want to find some sort of minimum 我们想找到某种最低要求
  4. The criterion is: we want to find the array which contains the element which has the minimal absolute difference from 1.8 , so we only have to consider the minimal distance of the three 判据是:我们要查找包含与1.8绝对差最小的元素的数组,因此我们仅需考虑三个元素之间的最小距离

Assumption 假设

In view of your comment, my understanding is that if: 鉴于您的评论,我的理解是:

a = [[:a, :b, 2], [:a, :b, 3], [:c, :d, 1], [:c, :d, 4]]

and the target value is 1.8 , the desired result is: 且目标值为1.8 ,则预期结果为:

[[:a, :b, 2]]

and not: 并不是:

[[:a, :b, 2], [:c, :d, 1]]

as the "closest value" is 2 and 因为“最接近的值”是2

[[:c, :d, 1], [:c, :d, 4]].flatten.include?(2) #=> false

(At the end, however, I offer a solution for the case where "closest "value" is to be obtained for every group of elements having the same first two elements.) (但是,最后,我为要为具有相同的前两个元素的每组元素获得“最接近的”值的情况提供解决方案。)

Code

If my assumption is correct, obtaining the desired result is simple: 如果我的假设是正确的,则获得所需结果很简单:

def extract_closest(a, target)
  closest = a.flatten.
              select { |e| e.respond_to?(:abs) }.
              min_by { |e| (e.to_f-target).abs }
  a.select { |e| e.include?(closest) }.uniq { |e| e[0,2] }
end

Examples 例子

target = 1.8

a1 = [[:a, :b, 1, 2, 3], [:a, :b, 5, 6, 7], [:a, :b, 8, 9, 2],
      [:c, :d, 1, 2, 9], [:c, :d, 1, 7, 8]]

(Notice that I've changed the last element of a1[2] from the example.) (注意,我已从示例中更改了a1[2]的最后一个元素。)

extract_closest(a1, target)
  #=> [[:a, :b, 1, 2, 3], [:c, :d, 1, 2, 9]]

a2 = [[:a, :b, 1, 2, 3], [:a, :b, 5, 6, 7], [:a, :b, 8, 9, 2],
      [:c, :d, 1, 3, 9], [:c, :d, 1, 7, 8]]

extract_closest(a2, target)
  #=> [[:a, :b, 1, 2, 3]]

Explanation 说明

For the first example above, the steps are as follows: 对于上面的第一个示例,步骤如下:

b = a1.flatten
  #=> [:a, :b, 1, 2, 3, :a, :b, 5, 6, 7, :a, :b, 8, 9, 10,
  #    :c, :d, 1, 2, 9, :c, :d, 1, 7, 8]
c = b.select { |e| e.respond_to?(:-) }
  #=> [1, 2, 3, 5, 6, 7, 8, 9, 10, 1, 2, 9, 1, 7, 8] 
closest = c.min_by { |e| (e.to_f-target).abs }
  #=> 2 

enum = a.select
  #=> #<Enumerator: [[:a, :b, 1, 2, 3], [:a, :b, 5, 6, 7],
  #     [:a, :b, 8, 9, 10], [:c, :d, 1, 2, 9], [:c, :d, 1, 7, 8]]
  #     :select> 
e = enum.next
  #=> [:a, :b, 1, 2, 3] 
e.include?(closest) 
  #=>   [:a, :b, 1, 2, 3].include?(2) => true

so [:a, :b, 1, 2, 3] is selected. 因此选择了[:a, :b, 1, 2, 3]

e = enum.next
  #=> [:a, :b, 5, 6, 7] 
e.include?(closest) 
  #=>   [:a, :b, 5, 6, 7].include?(2) #=> false

so [:a, :b, 5, 6, 7] is not select. 因此未选择[:a, :b, 5, 6, 7] The remaining elements of enum are processed similarly, resulting in a return value of: enum的其余元素的处理方式类似,返回值为:

f = [[:a, :b, 1, 2, 3], [:a, :b, 8, 9, 2], [:c, :d, 1, 2, 9]]

Lastly, we need only one of the first two elements of f , so: 最后,我们只需要f的前两个元素之一,所以:

f.uniq { |e| e[0,2] }
  #=> [[:a, :b, 1, 2, 3], [:c, :d, 1, 2, 9]] 

Alternative assumption 替代假设

If a "closest "value" is to be obtained for every group of elements having the same first two elements. In addition, to respond to your comment, I'll permit any of the elements of each array to be any object: 如果要为每组具有相同的前两个元素的元素获取“最接近的”值,另外,为了回应您的评论,我将允许每个数组的任何元素为任何对象:

def extract_closest(a, target)
  a.each_with_object({}) do |e,h|
    min_diff = e[2..-1].select { |n| n.respond_to?(:abs) }.
                        map { |n| (n.to_f-target).abs }.min
    h.update(e[0,2]=>[min_diff, e]) do |_,(omin,oe),(nmin,ne)|
      (nmin < omin) ? [nmin, ne] : [omin, oe]
    end
  end.values.map(&:last)
end

target = 1.8

a3 = [[:a, :b, 1, 2, :c], [:a, :b, 5, "c", 7], [:a, :b, 8, 9, [1,2]],
      [:c, :d, 1, 3, {e: 1.8}], [:c, :d, 1, 7, "8"]]

extract_closest(a3, target)
  #=> [[:a, :b, 1, 2, :c], [:c, :d, 1, 3, {:e=>1.8}]]

This uses the form of Hash#update (aka merge! ) that uses a block to determine the value of keys that are present in both hashes being merged. 这使用Hash#update的形式(又名merge! ),该形式使用一个块来确定要合并的两个哈希中存在的键的值。

Explanation for alternative assumption 替代假设的解释

For the example given: 对于给出的示例:

enum0 = a3.each_with_object({})
  #=> #<Enumerator: [[:a, :b, 1, 2, :c], [:a, :b, 5, "c", 7],
  #     [:a, :b, 8, 9, [1, 2]], [:c, :d, 1, 3, {:e=>1.8}],
  #     [:c, :d, 1, 7, "8"]]:each_with_object({})> 
e,h = enum0.next
  #=> [[:a, :b, 1, 2, :c], {}] 
e #=> [:a, :b, 1, 2, :c] 
h #=> {} 
b = e[2..-1].select { |n| n.respond_to?(:abs) }
  #=> [1, 2] 
c = b.map { |n| (n.to_f-target).abs }
  #=> [0.8, 0.2]
min_diff = c.min
  #=> 0.2
h.update(e[0,2]=>[min_diff, e]) do |_,(omin,oe),(nmin,ne)|
  (nmin < omin) ? [nmin, ne] : [omin, oe]
end
  #=> {}.update([:a, :b]=>[0.2, [:a, :b, 1, 2, :c]])
  #=> {[:a, :b]=>[0.2, [:a, :b, 1, 2, :c]]}

as {} does not contain the key [:a, :b] , update 's block is not called upon. 由于{}不包含键[:a, :b] ,因此不会调用update的块。 Note that update returns the updated value of h . 请注意, update返回h的更新值。

e,h = enum0.next
  #=> [[:a, :b, 5, "c", 7],
  #    {[:a, :b]=>[0.2, [:a, :b, 1, 2, :c]]}] 
b = e[2..-1].select { |n| n.respond_to?(:abs) }
  #=> [5, 7] 
c = b.map { |n| (n.to_f-target).abs }
  #=> [3.2, 5.2] 
min_diff = c.min
  #=> 3.2 
h.update(e[0,2]=>[min_diff, e]) do |_,(omin,oe),(nmin,ne)|
  (nmin < omin) ? [nmin, ne] : [omin, oe]
end

update first considers: update首先考虑:

h.update(e[0,2]=>[min_diff, e])
  #=> {[:a, :b]=>[0.2, [:a, :b, 1, 2, :c]]}.
  #     update([:a, :b]=> [3.2, [:a, :b, 5, "c", 7]])

As both hashes being merged are found to have the key [:a, :b] , update 's block is called upon to determine the value of that key in the merged hash: 由于发现两个合并的散列都具有键[:a, :b] ,因此将调用update的块以确定合并散列中该键的值:

  # do |_,(0.2, [:a, :b, 1, 2, :c]), (3.2, [:a, :b, 5, "c", 7])|
  #    (3.2 < 0.2) ? [3.2, [:a, :b, 5, "c", 7]] : [0.2, [:a, :b, 1, 2, :c]]
  # end

As 3.2 < 0.2 #=> false , the block returns: 3.2 < 0.2 #=> false ,该块返回:

[0.2, [:a, :b, 1, 2, :c]]

for the value of the key [:a, :b] ; 对于键[:a, :b] that is, the value is unchanged. 即,该值不变。

The remaining calculations are similar. 其余计算类似。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM