索引感知数组交集

时间:2015-09-09 13:12:13

标签: arrays ruby

我正在寻找一种数组交集的方法,与普通的&方法不同,它只匹配那两个数组中相同且具有相同索引的元素。 E.g:

["a", "b", "c", "d"].intersect(["a", "b", "f", "d", "c"])

应该返回

["a", "b", "d"]

因为所有三个数组都存在于相同索引(0,1和3)的两个数组中,而"c"被排除,因为它在两个数组(2和4)中具有不同的位置:

# 0    1    2    3    4
["a", "b", "c", "d"]
["a", "b", "f", "d", "c"]

我可以想到这个问题的几种解决方案。第一个是循环,另一个是首先修改数组以在元素中包含它们的索引,然后使用&进行交叉。 E.g:

["a", "b", "c"] #=> ["0a", "1b", "2c"]

然而,我想知道是否存在更好的方法。我正在寻找使用正负索引。

1 个答案:

答案 0 :(得分:7)

鉴于您的两个数组ab

a = ["a", "b", "c", "d"]
b = ["a", "b", "f", "d", "c"]

以下是不同的尝试:

使用zipselect

您可以使用zip组合两个数组和select中的相应元素来检索相同的元素:

a.zip(b).select { |x, y| x == y }.map(&:first)
#=> ["a", "b", "d"]

一步一步:

a.zip(b)                   #=> [["a", "a"], ["b", "b"], ["c", "f"], ["d", "d"]]
 .select { |x, y| x == y } #=> [["a", "a"], ["b", "b"], ["d", "d"]]
 .map(&:first)             #=> ["a", "b", "d"]

请注意,如果zip更短,nil将使用nil值填充第二个数组。如果您的数组包含a = [2, 1, nil] b = [2] a.zip(b).select { |x, y| x == y }.map(&:first) #=> [2, nil] 值,则可能会出现问题:

b

这是因为[2, nil, nil]zip内变为each_with_index

使用&[element, index]

或者您可以使用each_with_index构建ai = a.each_with_index.to_a #=> [["a", 0], ["b", 1], ["c", 2], ["d", 3]] bi = b.each_with_index.to_a #=> [["a", 0], ["b", 1], ["f", 2], ["d", 3], ["c", 4]] 对数组:

ai & bi
#=> [["a", 0], ["b", 1], ["d", 3]]

并改为相交:

map

我们可以使用(ai & bi).map(&:first) #=> ["a", "b", "d"] 来提取第一个元素:

(a.each_with_index.to_a & b.each_with_index.to_a).map(&:first)
#=> ["a", "b", "d"]

在单个表达式中:

each_with_index

使用each_objecteach_with_index

a.each_with_index.with_object([]) { |(x, i), arr| arr << x if x == b[i] } #=> ["a", "b", "d"] with_object结合起来的另一种方式:

x

a中的每个元素beach_twin中的相应元素进行比较,如果它们相等,则将其添加到结果数组中。

这可以避免创建中间数组,但我觉得它的可读性较差。

使用自定义枚举器

def each_twin(a, b) return enum_for(__method__, a, b) unless block_given? loop do x, y = a.next, b.next yield x if x == y end end each_twin(a.to_enum, b.to_enum).to_a #=> ["a", "b", "d"] 可能不是最好的名字(我在命名方面不好):

a

在循环中,从ba检索下一个值。如果b的值等于a,则会产生b的值。 Enumerator#next导致循环在{{1}}或{{1}}结束时退出。