使用自定义规则执行两个数组的并集

时间:2016-08-26 17:54:11

标签: arrays ruby set union

我有两个数组

b = ["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas"]

a = ["Jon Roberts", "Wil Koleva", "Lilian Joe", "Vic Jane", "Al Thomas"]

目前我在这两个数组上使用union运算符,如下所示:a | b。合并后,即使每个数组中的名称都是“相同”名称(它们只是使用缩写版本的名称),它也会复制我的名字。

我提出的解决方法是简单地选择第一个出现的第一个初始名称和姓氏作为执行联合的名称,但是,我不记得Ruby中有任何方法可以执行这样的操作。 / p>

因此some_method(a | b)的结果将返回c,这只是:

["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas"]

我想知道如何实现这一目标?

3 个答案:

答案 0 :(得分:1)

当然,只需将Enumerable#uniq与块一起使用:

c = (a | b).uniq do |full_name|
  first_name, last_name = full_name.split(nil, 2)
  [first_name[0], last_name]
end

答案 1 :(得分:1)

注意:代码的第一次迭代使用首字母而不是缩写名称。 也许你可以介绍Name的概念?它只是为uniq提供一个块而不是代码,但它很好地封装了所有相关的内容。

class Name
  def initialize(first, last)
    @first, @last = first, last
  end

  def abbreviated
    "#{@first[0]} #{@last}"
  end

  def eql?(other)
    return false if !other.respond_to?(:abbreviated)
    abbreviated == other.abbreviated
  end

  def hash
    abbreviated.hash
  end

  def full
    "#{@first} #{@last}"
  end
end

a = Name.new('John', 'Roberts')
b = Name.new('Jon', 'Roberts')
c = Name.new('William', 'Koleva')
d = Name.new('Wil', 'Koleva')

x = [a, c]
y = [b, d]

p (y | x).map(&:full)

值得注意的是,缩写的firstname不足以检查名称的相等性。

考虑:

Jim Jackson
James Jackson
Janine Jackson
...

答案 2 :(得分:1)

b = ["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas"]
a = ["Jon Roberts", "Wil Koleva", "Lilian Joe", "Vic Jane", "Al Thomas"]

r = /
    \s           # match a space
    [[:alpha:]]+ # match > 0 alphabetic characters
    \z           # match end of string
    /x           # free-spacing regex definition mode

(b+a).uniq { |str| [str[0], str[r]] }
  #=> ["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas"]

这使用了使用块的方法Array#uniq的形式。

您也可以写(b|a).uniq { |str| [str[0], str[r]] }

步骤如下。

c = b+a
  # => ["John Roberts", "William Koleva", "Lili Joe", "Victoria Jane", "Allen Thomas",
  # "Jon Roberts", "Wil Koleva", "Lilian Joe", "Vic Jane", "Al Thomas"] 

传递给块的c的第一个元素是

str = c.first
  #=> "John Roberts"

所以块计算是

[str[0], str[r]]
  #=> ["J", " Roberts"]

c的所有其他元素的计算类似。结果就是

c.uniq { |str| [str[0], str[r]] }

相当于选择转换为c[<first name initial>, <last name>]的第一个元素,它们匹配数组d的元素,其中

d = [["J", "Roberts"], ["W", "Koleva"], ["L", "Joe"], ["V", "Jane"], ["A", "Thomas"],
     ["J", "Roberts"], ["W", "Koleva"], ["L", "Joe"], ["V", "Jane"], ["A", "Thomas"]].uniq
  #=> [["J", "Roberts"], ["W", "Koleva"], ["L", "Joe"], ["V", "Jane"], ["A", "Thomas"]] 

Pascal建议uniq块更好地返回一个字符串:

{ |str| "#{str[0]} #{str[r]}" }

(例如,"J Roberts")可能改为编写

{ |str| str.sub(/(?<=.)\S+/,"") }

第一个首字母后面的空格是可选的(例如,"JRoberts"也可以。)