处理具有重复项的数组

时间:2017-06-14 06:52:40

标签: ruby

我有一个数组

a = ['A', 'B', 'B', 'C', 'D', 'D']

我必须通过所有元素,根据是否是最后一次出现来做某事,并在处理完后删除该元素。

如果重要,元素已经分类。

我正在寻找有效率的东西。有什么建议吗?

她到现在为止。这是预期的,但不确定它是否非常有效。

    a = ['A', 'B', 'B', 'C', 'D', 'D']

while !a.empty?
  b = a.shift

  unless a.count(b) > 0
    p "unique #{b}"
  else
    p "duplicate #{b}"
  end
end

并生成

"unique A"
"duplicate B"
"unique B"
"unique C"
"duplicate D"
"unique D"

由于

4 个答案:

答案 0 :(得分:4)

简单方法:

array = ["A", "B", "B", "C", "D", "D"]

array.group_by{|e| e}.each do |key,value| 
  *duplicate,  uniq = value
  duplicate.map do |e|
    puts "Duplicate #{e}"
  end
  puts "Unique #{uniq}"
end

根据 Stefan's comment 和建议,更短的方法是:

array.chunk_while(&:==).each do |*duplicate, uniq|
  duplicate.map do |e|
    puts "Duplicate #{e}"
  end
  puts "Unique #{uniq}"
end


# Above both will give the same Output:
---------------------------------------
Unique A
Duplicate B
Unique B
Unique C
Duplicate D
Unique D

答案 1 :(得分:1)

根据您的代码和预期输出,我认为这是您正在寻找的有效方式:

ALTER procedure [dbo].[usp_ExportData]
(
@StartDate Date,
@EndDate Date
)
AS
BEGIN
declare @sql varchar(max)
set @sql = ''
set @sql += ' Select REPLACE(U.EmployeeID, '','','') as 
EmployeeID,REPLACE(U.ClientID, '','','') as ClientID,REPLACE(U.ID, '','','') as ID,
REPLACE(U.FirstName, '','','')as FirstName,REPLACE(U.MiddleName, '','','')as MiddleName,
REPLACE(U.LastName, '','','')as LastName,REPLACE(U.Email, ',','')as 
Email,REPLACE(U.SSN, '','','')as SSN,
REPLACE(U.DateOfBirth, '','','')as DateOfBirth,REPLACE(U.Gender, '','','')as
Gender,REPLACE(U.CreatedDate, '','','')as CreatedDate,
REPLACE(U.ModifiedDate, '','','')as ModifiedDate, REPLACE(UPI.StreetAddress1,
 '','','')as StreetAddress1,
REPLACE(UPI.StreetAddress2, '','','')as StreetAddress2,REPLACE(UPI.City, '','','')as
 City,
REPLACE(UPI.State, '','','')as State,
REPLACE(UPI.ZipCode, '','','')as ZipCode,
REPLACE(UPI.CellPhoneNumber, '','','')as CellPhoneNumber, '' as Department, '' as 
JobTitle, '' as StreetAddress3 from Users U INNER JOIN PersonalContacts UPI ON
 U.ID= UPI.UserID'

if(@StartDate <> '' or @EndDate <> '')set @sql += ' where U.ModifiedDate >='''+@StartDate+''' and U.ModifiedDate<= '''+@EndDate+''''

exec(@sql)
END

但我想重申我的输出中的措辞与你的措辞的重要性。这与输入中的值是否唯一无关。这似乎是关于值是否是输入中的 last 出现。

答案 2 :(得分:1)

与@GaganGami的答案非常相似,但使用chunk_while

a.chunk_while { |a,b| a == b }
 .each do |*list,last|
   list.each { |e| puts "duplicate #{e}" }
   puts "unique #{last}"
 end
当元素发生变化时,

chunk_while将数组拆分为子数组。

['A', 'B', 'B', 'C', 'D', 'D'].chunk_while { |a,b| a == b }.to_a
# => [["A"], ["B", "B"], ["C"], ["D", "D"]] 

答案 3 :(得分:1)

OP表示a的元素已经排序,但我建议的方法不需要这样做。它还维护了数组顺序,这对于为要删除的每个元素执行的“执行某些操作”代码非常重要。它在数组已经排序的情况下没有性能损失。

对于数组

['A', 'B', 'D', 'C', 'B', 'D']

我假设要为'A'执行某些代码,'C'第二个'B'和第二个'D'按顺序执行之后是一个新数组

['B', 'D']

返回。

<强>代码

def do_something(e) end

def process_last_dup(a)    
  a.dup.
    tap do |b|
      b.each_with_index.
        reverse_each.
        uniq(&:first).
        reverse_each { |_,i| do_something(a[i]) }.
        each { |_,i| b.delete_at(i) }
    end
end

示例

a = ['A', 'B', 'B', 'C', 'D', 'D']

process_last_dup(a)
  #=> ["B", "D"]

<强>解释

步骤如下。

b = a.dup
  #=> ["A", "B", "B", "C", "D", "D"]
c = b.each_with_index
  #=> #<Enumerator: ["A", "B", "B", "C", "D", "D"]:each_with_index>
d = c.reverse_each
  #=> #<Enumerator: #<Enumerator: ["A",..., "D"]:each_with_index>:reverse_each>

请注意,d可以被视为“复合”枚举器。我们可以将它转换为数组,以查看它将生成的元素并传递给uniq

d.to_a
  #=> [["D", 5], ["D", 4], ["C", 3], ["B", 2], ["B", 1], ["A", 0]]

继续,

e = d.uniq(&:first)
  #=> [["D", 5], ["C", 3], ["B", 2], ["A", 0]]
e.reverse_each { |_,i| do_something(a[i]) }

reverse_each用于do_something首先执行'A',然后执行第二'B',依此类推。

e.each { |_,i| b.delete_at(i) }
b #=> ["B", "D"]

如果要对a进行修改,请将a.dup.替换为a.

读者可能已经注意到我在开头使用的代码Object#tap使得tap的块变量b(最初等于a.dup)将被返回在tap块内修改后,而不是在开头明确设置b = a.sup,在结尾处b明确设置,就像我在逐步说明中所做的那样。当然,这两种方法都会产生相同的结果。

Enumerable#uniq的文档未指定是否保留第一个元素,但它确实引用了Array.uniq,它确实保留了第一个元素。如果对此有任何不安,可以随时将reverse_each替换为reverse,以便Array.uniq使用。