def uniq_by
hash, array = {}, []
each { |i| hash[yield(i)] ||= (array << i) }
array
end
6
现在,如果您可以对属性值进行排序,则可以执行以下操作:
class A
attr_accessor :val
def initialize(v); self.val = v; end
end
objs = [1,2,6,3,7,7,8,2,8].map{|i| A.new(i)}
objs.sort_by{|a| a.val}.inject([]) do |uniqs, a|
uniqs << a if uniqs.empty? || a.val != uniqs.last.val
uniqs
end
class Array
def uniq_by(&blk)
transforms = []
self.select do |el|
should_keep = !transforms.include?(t=blk[el])
transforms << t
should_keep
end
end
end
class Foo
attr_accessor :foo, :bar, :baz
def initialize(foo,bar,baz)
@foo = foo
@bar = bar
@baz = baz
end
end
objs = [Foo.new(1,2,3),Foo.new(1,2,3),Foo.new(2,3,4)]
# find objects that are uniq with respect to attributes
objs.inject([]) do |uniqs,obj|
if uniqs.all? { |e| Marshal.dump(e) != Marshal.dump(obj) }
uniqs << obj
end
uniqs
end
13 回答
我找到的最优雅的方法是使用带有块的Array#uniq进行分拆
......它也读得更好!
我喜欢jmah和Head的答案 . 但他们保留阵列顺序吗?它们可能在更新版本的ruby中,因为已经有一些写入语言规范的哈希插入顺序保留要求,但这里是一个类似的解决方案,我喜欢使用它保留顺序,无论如何 .
ActiveSupport实现:
现在,如果您可以对属性值进行排序,则可以执行以下操作:
这是一个1属性的唯一,但同样的事情可以用词典排序...
Rails也有一个#uniq_by方法 - 见Parameterized Array#uniq (i.e., uniq_by)
使用带有块的Array#uniq:
将
uniq_by
方法添加到项目中的Array . 它与sort_by
类似 . 所以uniq_by
是uniq
,因为sort_by
是sort
. 用法:实施:
请注意,它返回一个新数组,而不是修改当前的数组 . 我们还没有编写
uniq_by!
方法,但如果你愿意,它应该很容易 .编辑:Tribalvibes指出该实现是O(n ^ 2) . 更好的是(未经测试)......
在数据库级别上执行:
我最初建议在Array上使用
select
方法 . 以机智:[1, 2, 3, 4, 5, 6, 7].select{|e| e%2 == 0}
给了我们[2,4,6]
回来了 .但是如果你想要第一个这样的对象,请使用
detect
.[1, 2, 3, 4, 5, 6, 7].detect{|e| e>3}
给了我们4
.不过,我不确定你的目的是什么 .
我喜欢jmah使用Hash来强制执行唯一性 . 以下是另外两种皮肤猫的方法:
这是一个很好的1-liner,但我怀疑这可能会更快一点:
您可以使用此技巧从数组中的几个属性元素中选择唯一:
如果我正确理解你的问题,我已经使用比较Marshaled对象的准hacky方法解决了这个问题,以确定是否有任何属性变化 . 以下代码末尾的注入将是一个示例:
您可以使用散列,每个键只包含一个值: