将数组拆分为(element =>剩余元素)对的方法
给定一个数组文字 ,我想创建一个哈希,其中键是数组中的元素,值是包含其他/剩余元素的数组。
输入:
[1, 2, 3]
输出:
{1=>[2, 3], 2=>[1, 3], 3=>[1, 2]}
如果我引入一个变量很容易:
arr = [1, 2, 3] arr.map { |i| [i, arr - [i]] }.to_h
但是使用数组文字,我能提出的唯一解决方案涉及instance_exec
或instance_eval
,这似乎是hackish:
[1, 2, 3].instance_exec { map { |i| [i, self - [i]] } }.to_h
我是否忽略了内置方法或明显的解决方案? group_by
, combination
, permutation
和partition
似乎没有帮助。
我想出了这样的事情:
[1,2,3].permutation.to_a.map{ |e| [e.shift, e] }.to_h
然而,这有一个缺陷:它多次分配相同的密钥,但由于你不关心内部元素的顺序,这可能是一个“足够好”的解决方案。
我有另一个想法。 这是这样的:
a = [1, 2, 3] a.combination(2).with_object({}) { |ar, h| h[(a - ar).first] = ar } # => {3=>[1, 2], 2=>[1, 3], 1=>[2, 3]}
Piotr Kruczek的修改版本。
[1,2,3].permutation.with_object({}) { |(k, *v), h| h[k] = v } # => {1=>[3, 2], 2=>[3, 1], 3=>[2, 1]}
我会选择Piotr的解决方案,但为了它的乐趣,我有一个不同的方法:
[1,2,3].inject([[],{}]) do |h_a, i| h_a[0] << i h_a[1].default_proc = ->(h,k){ h_a[0] - [k]} h_a end.last
不过,它更像是一种黑客而且不那么优雅。
以下是使用Object#tap的三种方法。 是否存在禁止其使用的论点?
如果数组包含重复,则所有三种方法都有效; 例如:
[1,2,2].... #=> {1=>[1, 2], 2=>[1, 1]}
#1
[1,2,2].tap do |a| a.replace(a.cycle.each_cons(a.size).first(a.size).map { |k,*v| [k,v] }) end.to_h #=> {1=>[2, 3], 2=>[3, 1], 3=>[1, 2]}
#2
[1,2,3].tap do |a| @h = a.map do |i| b = a.dup j = b.index(i) b.delete_at(j) [i,b] end.to_h end @h #=> {1=>[2, 3], 2=>[1, 3], 3=>[1, 2]}
#3
[1,2,3].map.with_index { |*e| e.reverse }.to_h.tap do |h| a = h.values h.replace(a.each_with_object({}) do |e,g| b = a.dup i = b.index(e) b.delete_at(i) g.update(e=>b) end) end #=> {1=>[2, 3], 2=>[1, 3], 3=>[1, 2]}
附录
后两种方法中的代码可以通过使用急需的方法Array#difference
来简化,正如我在这里的答案中所定义的那样。 例如,#3变为:
[1,2,3].map.with_index { |*e| e.reverse }.to_h.tap do |h| a = h.values h.replace(a.each_with_object({}) { |e,g| g.update(e=>a.difference([e])) }) end #=> {1=>[2, 3], 2=>[1, 3], 3=>[1, 2]}