【问题标题】:Swap keys in ruby hash在 ruby​​ 哈希中交换键
【发布时间】:2013-12-11 12:40:25
【问题描述】:

在 Ruby 中,如何交换 Hash 中的键?

假设我有以下哈希:

{:one=>1, :two=>2, :three=>3, :four=>4 }

我想变成的:

{:one=>1, :three=>2, :two=>3, :four=>4}

也就是说,交换键 :two 和 :three 但保持它们的值不变。

对此最有效的解决方案是什么?

【问题讨论】:

  • 保持订单重要吗?如果没有,只需交换值即可。
  • 您能解释一下请求背后的原因吗?
  • 保持订单很重要。在现实生活中,由于运行 mssql 存储过程,我得到了一个集合(哈希数组),然后由助手将其表示为 html 表。我需要更改几列的顺序。

标签: ruby hash hashmap


【解决方案1】:

最简单的方法是:

h = {:one => 1, :two => 2, :three => 3, :four => 4}
h[:two], h[:three] = h[:three], h[:two]

如果这是你需要经常做的事情,你可以在 Hash 上定义一个允许更漂亮语法的方法:

class Hash
  def swap!(a, b)
    self[a], self[b] = self[b], self[a] if key?(a) && key?(b)
    self
  end

  def swap(a, b)
    self.dup.swap!(a, b)
  end
end

但是请注意,这两种解决方案都将保留散列中键值对的顺序。如果你想实际交换键以及它们的值,你可以这样做:

class Hash
  def swap(a, b)
    self.inject(Hash.new) do |h, (k,v)|
      if k == a
        h[b] = self[a]
      elsif k == b
        h[a] = self[b]
      else
        h[k] = v
      end
      h
    end
  end
end
{:one => 1, :two => 2, :three => 3, :four => 4}.swap(:two, :three)
# results in {:one=>1, :three=>2, :two=>3, :four=>4}

虽然我不确定你为什么要这样做。

【讨论】:

  • +1 表示前两行。 (我认为您可以跳过其余部分。)除此之外:您可以考虑使用Enumerable#each_with_object,而不是inject,在这种情况下,使用inject,您需要将对象(这里是一个哈希)返回给迭代器块的末端。 each_with_object 没有必要这样做。
  • 每天学习新东西 :) 我什至不知道each_with_object,虽然我偶尔会梳理Enumerable 的方法来提醒自己。
  • 非常感谢@migimunz 的完整回答,用交换扩展哈希类!并且交换方法在我的情况下起到了作用。
  • 一种思考方式是在inject 的参数为{}[] 时使用each_with_object
  • Wastrox,如果您使用的是 Ruby 2.0,您可能需要考虑使用 Module#refine,而不是全局更改类 Hash。更多关于refinehere
【解决方案2】:

Perl 让这变得非常简单,但 Ruby 没有哈希切片,所以我们必须以更迂回的方式来做:

hash = {:one=>1, :two=>2, :three=>3, :four=>4 }
new_key_order = [:one, :three, :two, :four]

new_hash = Hash[new_key_order.zip(hash.values)]
# => {:one=>1, :three=>2, :two=>3, :four=>4}

这是可行的,因为 Ruby 会记住散列的插入顺序,因此 values 总是以原始顺序返回它们。如果您想在不依赖插入顺序的情况下执行此操作,只需稍作改动:

old_key_order = [:one, :two,   :three, :four]
new_key_order = [:one, :three, :two,   :four]

new_hash = Hash[new_key_order.zip(hash.values_at(*old_key_order))]
# => {:one=>1, :three=>2, :two=>3, :four=>4}

请注意,我对齐了键列以使更改的内容真正突出。这是我们在团队中所做的事情,以帮助在看起来非常相似的代码发生变化时使事情变得明显。

可以使用并行赋值,但是当您处理大量列或字段时,这会很快导致代码混乱。 定义输入顺序和输出顺序更容易,就像上面一样,所以你对映射有一个非常直观的引用,然后将它们传递给 zip 并让它完成艰苦的工作,然后将其强制回哈希。

顺便说一句,这是我在 Perl 中的做法。这是使用调试器:

perl -de 1

  DB<1> %hash = ('one' => 1, 'two' => 2, 'three' => 3, 'four' => 4)

  DB<2> x \%hash
0  HASH(0x7fceb94afce8)
   'four' => 4
   'one' => 1
   'three' => 3
   'two' => 2
  DB<3> @hash{'one', 'three', 'two', 'four'} = @hash{'one', 'two', 'three', 'four'}

  DB<4> x \%hash
0  HASH(0x7fceb94afce8)
   'four' => 4
   'one' => 1
   'three' => 2
   'two' => 3

基本上,Perl 具有检索或分配与 Ruby 的 values_at 等效的能力,方法是将哈希强制转换为数组并定义键的顺序。当您想要重组大量数据时,它是 Perl 中一个非常强大的工具。

【讨论】:

    【解决方案3】:

    哈希中没有顺序的概念。

    【讨论】:

    • 这曾经是正确的,然而,自从 Ruby 1.9 以来,Ruby 维护了插入顺序。但它不是排序哈希。
    • @theTinMan 我不是在谈论 Ruby,而是在谈论哈希语义。确实,实现可能有顺序的概念,但这不是你应该建立的。
    • 实际上,我们保证 Ruby 的哈希插入顺序会保持不变。首次实施时,对此进行了很多讨论。其他语言可能会做一些不同的事情,但我们在这里讨论的是 Ruby。
    • 我同意在哈希中利用键的顺序是非常冒险的(特别是如果它被保存在数据库中)。不幸的是,这个问题是关于另一件事的。
    猜你喜欢
    • 2014-05-23
    • 1970-01-01
    • 2014-01-02
    • 2011-10-19
    • 2011-07-01
    • 2011-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多