【问题标题】:How do you narrow down a series of arrays?如何缩小一系列数组的范围?
【发布时间】:2011-04-04 17:51:52
【问题描述】:

我有几个数组。而且我想缩小它们的范围,这样最后,无论结果如何,只要这些数组不是 nil,就会在所有其他数组中看到值。

我在编码方面的悲伤尝试:

array_of_users = []
array_of_users & @zip_ids if !@zip_ids.empty?
array_of_users & @sex_ids if !@sex_ids.empty?
array_of_users & @interest_ids if !@interest_ids.empty?
array_of_users & @age_ids if !@age_ids.empty?

从逻辑上讲,我会认为这会起作用,因为只要它不为空,它就会在每一个中找到相似性,但它实际上并没有将它们添加到数组中。

你会如何做到这一点?

【问题讨论】:

  • [] 和其他任何东西的交集总是[]。从您的示例中,我无法真正说出您要尝试做什么。看起来您想根据各种条件过滤数组?也许Array#select 会更合适?
  • 只是为了澄清-您只需要所有数组中的值,而不是唯一值的列表,对吗?用数学术语来说,你想要一个交集,而不是一个并集?
  • @Chris,是的,我想要一个十字路口。但它必须是一个将数据保存到array_of_users 中的交叉点。如您所见,@cam 的评论是正确的,[] 的交集仍然是[]

标签: ruby-on-rails ruby arrays


【解决方案1】:

我想你想要这个:

users = [@zip_ids,@sex_ids,@interest_ids,@age_ids].reject(&:empty?).reduce(&:&)

【讨论】:

  • 嗯..这两个都没有产生任何结果。
  • 哦,抱歉,如果第一个数组为空,第一个数组就会失败。我删了它。第二个(现在只有一个)应该没问题,并且可以在我的测试中使用。
  • 这很奇怪,因为我觉得@digitalRoss 的回答很好,但由于某种原因,两个 asnwers 都返回了一个 empty 数组。
  • 啊,我明白了。这是因为它们必须被压扁才能变空。谢谢格伦!
【解决方案2】:

这样想:

([1] | [2] | [3,4]) & [3,4]

=> [3,4]

所以你可以这样做:

array_of_users = @zip_ids | @sex_ids | @interest_ids | @age_ids

intersection = array_of_users & @zip_ids & @sex_ids & @interest_ids & @age_ids

只是,就像@glenn 所说,它忽略了“如果为空则不合并”的要求。

@DigitalRoss 很好,但如果第一个数组是空的,一切都会变平。

那么,来自@glenn:

[[],[1,2],[2],[2,3,4,4],[]].reject(&:empty?).reduce(&:&)

=> [2]

【讨论】:

  • 这行不通,因为您忘记了非空部分。当你只打算再次相交它们时,合并所有这些数组是浪费时间......
【解决方案3】:

您需要将它们实际分配给数组。在您的代码所在的位置,您正在执行交集,但实际上并没有将它们分配给数组。您需要在其中添加 = 运算符。

这是一个示例设置:

>> x = [ 1, 1, 3, 5 ] 
>> y = [ 1, 2, 3 ]

这就是你现在正在做的事情:

>> x & y
=> [1, 3]
>> x
=> [1, 1, 3, 5]

这是它需要去的地方:

>> x &= y
=> [1, 3]
>> x
=> [1, 3]

因此,这应该可以完成工作:

array_of_users = []
array_of_users &= @zip_ids if !@zip_ids.empty?
array_of_users &= @sex_ids if !@sex_ids.empty?
array_of_users &= @interest_ids if !@interest_ids.empty?
array_of_users &= @age_ids if !@age_ids.empty?

希望这会有所帮助- 旁注:我在 IRB(交互式 ruby​​ shell)中完成了所有这些工作。是你的朋友。 :)

【讨论】:

  • 您需要将array_of_users 初始化为某个值,否则每次都会与一个空数组相交...
  • 啊,是的,很好的抓住格伦-我应该提到这一点。谢谢!
  • 这个其实不行,因为array_of_users是一个空数组,所以只能和其他空数组相交。
  • 是的,这就是格伦提到的,我猜想。 - 我的假设是 array_of_users 已经填充了您想要缩小范围的用户集。如果那不是真的,我可以适当地调整我的答案。 :)
【解决方案4】:
[@z, @s, @i, @a].reject(&:empty?).inject { |m, e| m & e }

【讨论】:

  • 这对我来说是我得到的任何答案中最有意义的,但它仍然返回 empty 值。
  • 不。这和我第一次写的一样。如果第一个数组为空,则不起作用,因为在inject 这种形式中,可枚举的第一个元素用于初始化变量,而不是通过块传递。
【解决方案5】:

您可以阅读here 方法 Array#& 返回一个新数组并且不修改原始数组,因此您必须将代码更改为:

假设您想要包含在所有四个非空数组中的 ID,我会尝试这样的事情:

ary = []
ids = [@zip_ids, @sex_ids, @interest_ids, @age_ids]
ids.each {|i|ary << i unless i.empty?}
ary.uniq!
result = ids.inject(ary){|res, ids| res & ids}

【讨论】:

  • 很好的回应!一切正常,直到最后一行。结果只返回ids
【解决方案6】:

正如 Glenn 所建议的,最惯用的方法可能是

users = [@zip_ids, @sex_ids, @interest_ids, @age_ids].reject(&:empty?).reduce(&:&)

但是,这取决于#hash 和#eql 的行为?对于数组中的对象。由于 Object#hash 的实现基于 object_id,因此用户对象的 Array#&(可能使用默认的 Object#hash impl)将比较对象 ID,而不是为您的对象定义相等语义的任何值。

允许您比较用户对象数组而不仅仅是它们的 ID 的解决方案是定义 #hash 和 #eql?对象的语义,然后使用 Array#&。例如:

class User
  # ...

  def hash
    [self.class, *equality_attributes].map(&:hash).reduce(:^)
  end

  def eql?(other)
    self.class == other.class &&
      self.equality_attributes == other.equality_attributes
  end

  # ...

  private

  def equality_attributes
    [name, address]
  end
end

【讨论】:

    猜你喜欢
    • 2017-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-25
    • 2021-09-03
    • 1970-01-01
    • 2015-06-09
    • 2019-07-07
    相关资源
    最近更新 更多