【问题标题】:Unique on an array of hashes based on value在基于值的哈希数组上唯一
【发布时间】:2011-06-17 23:47:42
【问题描述】:

我觉得这可以改进(在 ruby​​ 中很常见)。我正在尝试根据值对哈希数组进行 uniq。在这个例子中,我想要元素的颜色。苔藓和雪是冒名顶替者。

# remove unique array of hashes based on a hash value

a = [
  { :color => "blue", :name => "water" },
  { :color => "red", :name => "fire" },
  { :color => "white", :name => "wind" },
  { :color => "green", :name => "earth" },
  { :color => "green", :name => "moss" },
  { :color => "white", :name => "snow" }
]

# remove moss and snow
uniques = []
a.each_with_index do |r, i|
  colors = uniques.collect {|e| e[:color]}

  if !colors.include? r[:color]
    uniques.push r
  else
    a[i] = nil
  end
end

a.compact!

puts a

这将打印出来

{:color=>"blue", :name=>"water"}
{:color=>"red", :name=>"fire"}
{:color=>"white", :name=>"wind"}
{:color=>"green", :name=>"earth"}

这是“正确的”,但我觉得这太过分了。我对 .map .inject 的经验是有限的,那些先进的技术让我望而却步。如果有人可以重新考虑这一点,它可能会帮助我理解另一种简洁的技术。

【问题讨论】:

    标签: ruby


    【解决方案1】:

    在 Ruby 1.9 中,尝试以下操作

    a.uniq! {|e| e[:color] }
    

    【讨论】:

    • 它没有解决 OPs 的问题。虽然它过滤了独特的颜色,但如果数组顺序不同,它不会拒绝非元素。
    • 我回答了 OPs 的原始问题“根据值对哈希数组进行 uniq”,或者如引用所述“根据哈希值删除唯一的哈希数组”。模棱两可的是这个例子:它去除了“苔藓和雪”,但没有说明它们被去除的原因。我认为它们被删除是因为它们是重复的颜色:这就是原始代码删除它们的原因。 @the Tin Man 假设它们已被删除,因为由于示例的解释,它们不是元素。
    • "假定由于示例的解释,它们不是元素,因此被删除了",不,OP 说,"在这个示例中,我想要元素的颜色。苔藓和雪是冒名顶替者。 ",并且在示例中显示它们已被删除。元素是“火”、“气”、“土”和“水”。如果数组更改,此解决方案将失败;我的答案末尾有一个例子。
    • 对不起,我应该指定示例数据中的顺序很重要。在我的具体情况下,我对它们进行排序并且不介意我是否丢失了第二个值。感谢您提供更通用的解决方案 Tin Man,它当然更可靠。
    • 请注意,还有一个非破坏性版本:#uniq { ... }
    【解决方案2】:

    我会使用 Array 的 rejectselect 方法:

    require 'pp'
    
    a = [
      { :color => "blue", :name => "water" },
      { :color => "red", :name => "fire" },
      { :color => "white", :name => "wind" },
      { :color => "green", :name => "earth" },
      { :color => "green", :name => "moss" },
      { :color => "white", :name => "snow" }
    ]
    
    pp a.reject{ |h| %w[moss snow].include?( h[:name]) } 
    # >> [{:color=>"blue", :name=>"water"},
    # >>  {:color=>"red", :name=>"fire"},
    # >>  {:color=>"white", :name=>"wind"},
    # >>  {:color=>"green", :name=>"earth"}]
    

    或者,您可以对此持积极态度,并select您想保留的人:

    pp a.select{ |h| %w[water fire wind earth].include?( h[:name] ) } 
    # >> [{:color=>"blue", :name=>"water"},
    # >>  {:color=>"red", :name=>"fire"},
    # >>  {:color=>"white", :name=>"wind"},
    # >>  {:color=>"green", :name=>"earth"}]
    

    您并没有真正处理哈希,它是一个恰好包含哈希的数组,所以不要让它们混淆您。像 rejectselect 这样的数组方法是过滤掉不需要的元素或保留想要的元素的核心方法。

    在您的代码示例中,您忽略了目标是什么:您想要元素,拒绝“苔藓”和“雪”,它们是非元素。过滤掉非元素,然后在散列中留下正确/真实的元素。从那里您可以提取正确的颜色。

    使用uniq 时需要注意的另一个问题是它是位置性的,换句话说,它查找第一个唯一值并拒绝后续值。这在您的代码中并不明显,因为您的数组始终与您测试的顺序相同。如果你洗牌了……:

    2.times do
      pp a.shuffle.uniq{ |h| h[:color] }
    end
    

    通过#1...

    # [{:color=>"red", :name=>"fire"},
    #  {:color=>"white", :name=>"wind"},
    #  {:color=>"green", :name=>"moss"},
    #  {:color=>"blue", :name=>"water"}]
    

    通过 #2...

    # [{:color=>"green", :name=>"earth"},
    #  {:color=>"blue", :name=>"water"},
    #  {:color=>"red", :name=>"fire"},
    #  {:color=>"white", :name=>"snow"}]
    

    我们突然看到“苔藓”和“雪”都潜入结果中,尽管颜色是独一无二的。这些是您必须注意的微妙问题。

    【讨论】:

      【解决方案3】:

      对于任何可能想要更短的correct answer by Steve Wilhelm 变体的人,

      注意

      a.uniq!(&:color)
      

      不适用于散列数组,就像

      a[1].color
      

      也不行。

      有关 & 运算符的更多信息,请阅读 this linkthis question 上的 cmets,它们又包含大量资源链接。

      另一方面,您可以使用 lambdas 使 Symbol#to_proc 方法工作,正如 here 所解释的那样,尽管它可能只是使事情复杂化,而且肯定不会是正确答案的较短版本。但是,这是非常有趣的知识。

      感谢 mukesh-kumar-gupta 提供heads-up

      【讨论】:

      • 这在散列数组的情况下不起作用。使用@Steve Wilhelm 方法。
      • 你是对的。我会看看如何编辑它,这样其他人就不会陷入和我一样的错误。
      • 编辑您的原始答案并将旧答案替换为并更正。
      猜你喜欢
      • 2011-02-15
      • 1970-01-01
      • 1970-01-01
      • 2018-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-13
      • 1970-01-01
      相关资源
      最近更新 更多