如何在ruby中找到第一个值的数组之间的并集

时间:2015-03-06 18:05:21

标签: ruby arrays

我有一组像这样的数组:

[["1","2"],["1","3"],["2","3"],["2","5"]]

我想找到像

这样的第一个值的并集

["1","2"],["1","3"]匹配,因此我需要创建像["1","2,3"]

这样的新数组

所以生成的数组就像

[["1","2,3"],["2","3,5"]]

3 个答案:

答案 0 :(得分:4)

与Ruby中的大多数问题一样,Enumerable模块完成了这项工作:

input = [["1","2"],["1","3"],["2","3"],["2","5"]]

result = input.group_by do |item|
  # Group by first element
  item[0]
end.collect do |key, items|
  # Compose into new format
  [
    key,
    items.collect do |item|
      item[1]
    end.join(',')
  ]
end

puts result.inspect
# => [["1", "2,3"], ["2", "3,5"]]

汇总类似的内容时,group_by方法非常方便,collect非常适合重写元素的显示方式。

答案 1 :(得分:2)

你所要求的并不是每个2的真正联盟的真正联盟:

data = [["1","2"],["1","3"],["2","3"],["2","5"]]
data.each_slice(2).map{|a,b| a | b.to_a }
#=> [["1","2","3"],["2","3","5"]]

这是一个非常简单的解决方案,可以根据您的需求修改此概念:

data = [["1","2"],["1","3"],["2","3"],["2","5"]]
data.each_slice(2).map do |a,b|
  unified = (a | b.to_a)
  [unified.shift,unified.join(',')]
end
#=>  [["1", "2,3"], ["2", "3,5"]]

如果阵列数量不均匀,则将to_a添加到管道变量b。例如

 data = [["1","2"],["1","3"],["2","3"],["2","5"],["4","7"]]
 data.each_slice(2).map do |a,b|
   unified = (a | b.to_a)
   [unified.shift,unified.join(',')]
 end
 #=>  [["1", "2,3"], ["2", "3,5"], ["4","7"]]

如果你的意思是你希望这种情况发生而不管订单如何,那么这将有效但会破坏data对象

data.group_by(&:shift).map{|k,v| [k,v.flatten.join(',')]}
#=>  [["1", "2,3"], ["2", "3,5"], ["4","7"]]

非破坏性地你可以打电话

data.map(&:dup).group_by(&:shift).map{|k,v| [k,v.flatten.join(',')]}
#=>  [["1", "2,3"], ["2", "3,5"], ["4","7"]]

答案 2 :(得分:1)

这是另一种方式。

<强>代码

def doit(arr)
  arr.each_with_object({}) { |(i,*rest),h| (h[i] ||= []).concat(rest) }
     .map { |i,rest| [i, rest.join(',')] }
end

<强>实施例

arr1 = [["1","2"],["1","3"],["2","3"],["2","5"]]
doit(arr1)
  #=> [["1", "2,3"], ["2", "3,5"]]

arr2 = [["1","2","6"],["2","7"],["1","3"],["2","3","9","4","cat"]]
doit(arr2)
  # => [["1", "2,6,3"], ["2", "7,3,9,4,cat"]]

<强>解释

对于上面的arr1,我们获得:

enum = arr1.each_with_object({})
  #=> #<Enumerator: [["1", "2"], ["1", "3"], ["2", "3"],
  #                  ["2", "5"]]:each_with_object({})> 

我们可以将enum转换为数组,查看其元素:

enum.to_a
  #=> [[["1", "2"], {}], [["1", "3"], {}],
  #    [["2", "3"], {}], [["2", "5"], {}]]

这些元素将被传递到块中,并由Enumerator#each分配给块变量,它将调用Array#each。可以通过在[["1", "2"], {}]上调用Enumerator#next来获取这些元素中的第一个(enum):

(i,*rest),h = enum.next
     #=> [["1", "2"], {}] 
i    #=> "1" 
rest #=> ["2"] 
h    #=> {} 

然后我们执行:

(h[i] ||= []).concat(rest)
  #=> (h["1"] ||= []).concat(["2"]) 
  #=> (nil ||= []).concat(["2"]) 
  #=> [].concat(["2"]) 
  #=> ["2"] 

each然后将enum的下一个元素传递给块:

(i,*rest),h = enum.next
  #=> [["1", "3"], {"1"=>["2"]}]
i    #=> "1" 
rest #=> ["3"] 
h    #=> {"1"=>["2"]}
(h[i] ||= []).concat(rest)
  #=> (h["1"] ||= []).concat(["3"]) 
  #=> (["2"] ||= []).concat(["3"]) 
  #=> ["2"].concat(["3"]) 
  #=> ["2", "3"]

enum的最后两个元素传递给块后,我们得到:

h=> {"1"=>["2", "3"], "2"=>["3", "5"]} 

map创建一个枚举器:

enum_h = h.each
  #=> > #<Enumerator: {"1"=>["2", "3"]}:each> 

并调用Enumerator#each(调用Hash#each)将enum_h的每个元素传递到块中:

i, rest = enum_h.next 
  #=> ["1", ["2", "3"]] 

然后计算:

[i, rest.join(',')] 
  #=> ["1", ["2", "3"].join(',')]
  #=> ["1", "2,3"]

enum_h的其他元素的处理方式相似。