将数组拆分为(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_execinstance_eval ,这似乎是hackish:

 [1, 2, 3].instance_exec { map { |i| [i, self - [i]] } }.to_h 

我是否忽略了内置方法或明显的解决方案? group_bycombinationpermutationpartition似乎没有帮助。

我想出了这样的事情:

 [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]}