在Ruby中没有辅助变量的情况下向数组添加唯一元素

时间:2016-08-29 23:36:48

标签: arrays ruby variables hash

我有一系列哈希,比如

@data_records = [
                 {"user": "user1", "key1": "v1k1", ... , "keyN": "v1kN"},
                 {"user": "user2", "key1": "v2k1", ... , "keyN": "v2kN"},
                 {"user": "user3", "key1": "v3k1", ... , "keyN": "v3kN"},
                 {"user": "user1", "key1": "v4k1", ... , "keyN": "v4kN"},
                 {"user": "user1", "key1": "v5k1", ... , "keyN": "v5kN"},
                 {"user": "user4", "key1": "v6k1", ... , "keyN": "v6kN"},
                ]

正如您所看到的,我可能会为同一个用户提供许多“记录”。在上面的示例中,user1有三个记录,例如。

现在我需要基于这个散列数组生成一个数组,其中包含每个用户的单个条目。我的意思是,我需要

[ "user1", "user2", "user3", "user4" ]

但不是

[ "user1", "user2", "user3", "user1", "user1", "user4" ].

我编写了以下代码,完成了这项工作:

def users_array
  arr = Array.new
  @data_records.each { |item| arr.push(item["user"]) if not arr.include?(item["user"])}
  arr
end

但令我困扰的是,我必须使用辅助变量arr来实现这一目标。我确信使用Array#map方法有一个更短的方法。由于Array#map返回一个数组,它可能类似于

def users_array
  @data_records.map { |item| item["user"] if ... }
end

问题是我不知道如何提到我在块中使用Array#map创建的数组。我相信它可能像

def users_array
  @data_records.map { |item| item["user"] if not this.include?(item["user"]) }
end

但当然不行。

有人可以判断是否有办法做到这一点?

修改

是的,我可以使用Array#uniq来执行此操作。但后来我重新解释了这个问题:有没有办法在地图块中引用由map创建的隐式数组?

3 个答案:

答案 0 :(得分:3)

对我来说,最好的方法是Set,并使用require 'set' def users_array @data_records.each_with_object(Set.new) do |item, set| set << item[:user] end end 代替数组来收集用户名。

An error occurred when converting the 'mymsmqservermachine.mydomain.network.ads\private$\Path/To/MyQueuedService.svc' queue path name to the format name: Unrecognized error -1072824300 (0xc00e0014). All operations on the queued channel failed. Ensure that the queue address is valid. MSMQ must be installed with Active Directory integration enabled and access to it is available.

答案 1 :(得分:2)

Aetherus最接近回答你的改述问题,他应该得到所有的信任,指出#each_with_object得到“隐式阵列”。但是这里的内容更接近你的要求:

@data_records.each_with_object([]) do |item, this|
  this << item[:user] unless this.include?(item[:user])
end

我认为使用Set:

Set.new(@data_records.map { |item| item[:user] })

#uniq

@data_records.map { |item| item[:user] }.uniq

可能会更快,并且可以更好地扩展到大量项目,但我还没有对它进行基准测试。

答案 2 :(得分:1)

编辑:我担心我可能误解了这个问题。

如果有兴趣的话,我会留下原来的答案(如下)。

def combine(data, key)
  data.each_with_object({}) do |g,h|
    f = g.each_with_object({}) { |(k,v),f| f[k] = (k==:user ? v : [v]) }
    h.update(f[:user]=>f) do |k,o,n|
      o.merge(n) { |kk,nn,oo| kk==:user ? nn : nn+oo } 
    end
  end.values
end 

data_records = [
  {user: "user1", key1: "v1k1", keyN: "v1kN"},
  {user: "user2", key1: "v2k1", keyN: "v2kN"},
  {user: "user3", key1: "v3k1", keyN: "v3kN"},
  {user: "user1", key1: "v4k1", keyN: "v4kN"},
  {user: "user1", key1: "v5k1", keyN: "v5kN"},
  {user: "user4", key1: "v6k1", keyN: "v6kN"},
]

combine(data_records, :user)
  #=> [{:user=>"user1", :key1=>["v1k1", "v4k1", "v5k1"],
  #     :keyN=>["v1kN", "v4kN", "v5kN"]},
  #    {:user=>"user2", :key1=>["v2k1"], :keyN=>["v2kN"]},
  #    {:user=>"user3", :key1=>["v3k1"], :keyN=>["v3kN"]},
  #    {:user=>"user4", :key1=>["v6k1"], :keyN=>["v6kN"]}]