【问题标题】:Method for splitting array into (element => remaining elements) pairs将数组拆分为(元素=>剩余元素)对的方法
【发布时间】:2015-05-08 10:25:56
【问题描述】:

给定一个数组文字,我想创建一个散列,其中键是数组中的元素,值是包含其他/剩余元素的数组。

输入:

[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,这似乎很老套:

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

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

【问题讨论】:

  • 不相关,但既然你不传递任何参数来阻止,为什么不instance_eval
  • @AndreyDeineko 有区别吗?
  • [[1,2,3]] * 2 ⇒ 现在你有一个副本:)
  • @Stefan 在instance_evalinstance_exec 之间 - 是的:)
  • @mudasobwa tap 不会返回原始数组吗?

标签: ruby


【解决方案1】:

我想出了这样的东西:

[1,2,3].permutation.to_a.map{ |e| [e.shift, e] }.to_h

但是这有一个缺陷:它多次分配相同的键,但由于您不关心内部元素的顺序,这可能是一个“足够好”的解决方案。

【讨论】:

  • 这是一个有趣的解决方案。
  • 确实,这是一种很有创意的方法 :-)
  • 可以省略to_a,用*分割数组:[1,2,3].permutation.map { |k, *vs| [k, vs] }.to_h
【解决方案2】:

我有另一个想法。这是这个:

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

【讨论】:

  • 组合是我一开始想到的,但这里的问题是作为参数你必须传递a.length-1,而重点不是将数组分配给变量:/
  • @PiotrKruczek 好的.. 我没有阅读完整的问题.. 所以,如果我将它分配给一个变量 life is easy.. :)
【解决方案3】:

我会选择 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

不过,它更像是一种 hack 并且不太优雅。

【讨论】:

  • 虽然默认 proc 返回正确的值,但我无法枚举 keys 或使用 each 迭代哈希。
【解决方案4】:

这里有三种使用Object#tap的方法。是否有禁止使用它的论据?

如果数组包含 dups,则所有三种方法都有效;例如:

[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来简化,正如我在回答here中定义的那样。例如,#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]}

【讨论】:

  • tap 绝对比instance_exec / instance_eval 好;)
【解决方案5】:
[1,2,3,4].each_with_object({}) do |n,h|
  h.each_key { |k| h[k] << n }
  h[n] = h.keys
end
  #=> {1=>[2, 3, 4], 2=>[1, 3, 4], 3=>[1, 2, 4], 4=>[1, 2, 3]}

【讨论】:

    猜你喜欢
    • 2020-11-14
    • 2019-05-31
    • 1970-01-01
    • 2020-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多