If one has an array of objects, each containing various values, what
means are there to find all the objects where one of these particular
values is unique?
I have done this using a hash to record values I've seen before (code
attached), but wondered if there might be another way, e.g. using
collect.
Attachments:
http://www.ruby-forum.com/attachment/76/modeltest.rb
···
--
Posted via http://www.ruby-forum.com/.
If one has an array of objects, each containing various values, what
means are there to find all the objects where one of these particular
values is unique?
And only those unique values?
names = names.uniq # or
names.uniq! # I'm using the same variable name used in your
# example code
···
On Thursday 09 August 2007 02:24:46 am Milo Thurston wrote:
I have done this using a hash to record values I've seen before (code
attached), but wondered if there might be another way, e.g. using
collect.
Attachments:
http://www.ruby-forum.com/attachment/76/modeltest.rb
--
Konrad Meyer <konrad@tylerc.org> http://konrad.sobertillnoon.com/
Konrad Meyer wrote:
And only those unique values?
Not only those, but the entire objects.
I could use something like
names = things.collect {|x| x.name}.uniq
to just get the names, but I need the rest of the data in the object
also.
The code I posted works, but I'm not sure that it's the best solution.
···
--
Posted via http://www.ruby-forum.com/\.
Why don't you use #select?
selection = things.select {|x| x.name = "foo"}
Did I misunderstand your requirement?
Kind regards
robert
···
2007/8/9, Milo Thurston <knirirr@gmail.com>:
Konrad Meyer wrote:
> And only those unique values?
Not only those, but the entire objects.
I could use something like
names = things.collect {|x| x.name}.uniq
to just get the names, but I need the rest of the data in the object
also.
The code I posted works, but I'm not sure that it's the best solution.
Robert Klemme wrote:
selection = things.select {|x| x.name = "foo"}
That is closer, but still not quite it.
A slight change of my previously posted code may make it clearer. In
this case I start with an array of "Thing" objects called "things", and
would like an array called "uniquely_named_things" containing Things
where the name is unique.
check = Hash.new
uniquely_named_things = Array.new
things.each do |s|
if check[s.name].nil?
uniquely_named_things << s
end
check[s.name] = 1
end
Basically, I wonder if anything that does the same as this has already
been included in Ruby.
···
--
Posted via http://www.ruby-forum.com/\.
Hi --
Robert Klemme wrote:
selection = things.select {|x| x.name = "foo"}
That is closer, but still not quite it.
A slight change of my previously posted code may make it clearer. In
this case I start with an array of "Thing" objects called "things", and
would like an array called "uniquely_named_things" containing Things
where the name is unique.
check = Hash.new
uniquely_named_things = Array.new
things.each do |s|
if check[s.name].nil?
uniquely_named_things << s
end
check[s.name] = 1
end
Basically, I wonder if anything that does the same as this has already
been included in Ruby.
I don't think so. It might be handy to generalize it:
module UniqBy
def uniq_by
res =
count = Hash.new(0)
each do |item|
y = yield(item)
if y
count[y] += 1
if count[y] == 1
res << item
else
res.delete(item)
end
end
end
res
end
end
Thing = Struct.new(:name)
a = Thing.new("David")
b = Thing.new("John")
c = Thing.new("David")
d = Thing.new("Mary")
e = Thing.new("Joe")
things = [a,b,c,d,e].extend(UniqBy)
p things.uniq_by {|thing| thing.name }
Another way to do this, which I imagine is much slower, is:
things.select do |thing|
things.select {|other| other.name == thing.name }.size == 1
end
David
···
On Thu, 9 Aug 2007, Milo Thurston wrote:
--
* Books:
RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
& consulting: Ruby Power and Light, LLC (http://www.rubypal.com)
Robert Klemme wrote:
> selection = things.select {|x| x.name = "foo"}
That is closer, but still not quite it.
A slight change of my previously posted code may make it clearer. In
this case I start with an array of "Thing" objects called "things", and
would like an array called "uniquely_named_things" containing Things
where the name is unique.
check = Hash.new
uniquely_named_things = Array.new
things.each do |s|
if check[s.name].nil?
uniquely_named_things << s
end
check[s.name] = 1
end
Your code seems to implement something different from your wording.
Your wording says "keep only things whose name is not used by another
thing". Your code does "keep one thing per unique name".
Basically, I wonder if anything that does the same as this has already
been included in Ruby.
There are numerous ways to achieve that. As I am a big fan of #inject,
this is probably my first choice:
# 1. keep only one thing per name
selection = things.inject({}) {|h,th| h[th.name] ||= th; h}.values
# 2. keep things whose name is unique
selection = things.
inject(Hash.new {|h,k| h[k]=}) {|h,th| h[th.name] << th; h}.
select {|k,v| v.size == 1}.
map {|k,v| v}
# 2. alternative impl.
selection.things.
inject({}) do |h,th|
h[th.name] = h.has_key? th.name ? nil : th
h
end.values.compact
If I am not mistaken David's code implements the second solution
similar to my alternative implementation.
Kind regards
robert
···
2007/8/9, Milo Thurston <knirirr@gmail.com>:
Hi --
# 2. alternative impl.
selection.things.
I think you mean selection = things 
inject({}) do |h,th|
h[th.name] = h.has_key? th.name ? nil : th
When I tried your code I found you need parens:
h[th.name] = h.has_key?(th.name) ? nil : th
Otherwise it's like:
h[th.name] = h.has_key? (th.name ? nil : th)
and you get [false, false, false, ...].
David
···
On Thu, 9 Aug 2007, Robert Klemme wrote:
--
* Books:
RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
& consulting: Ruby Power and Light, LLC (http://www.rubypal.com)
Robert Klemme wrote:
Your code seems to implement something different from your wording.
Your wording says "keep only things whose name is not used by another
thing". Your code does "keep one thing per unique name".
It is my code that gives the correct meaning in this case. Evidently it
is more precise that English. 
···
--
Posted via http://www.ruby-forum.com/\.
Good catches! Thanks! /me confesses I did not try - just check for syntax. :-}
robert
···
2007/8/9, dblack@rubypal.com <dblack@rubypal.com>:
Hi --
On Thu, 9 Aug 2007, Robert Klemme wrote:
> # 2. alternative impl.
> selection.things.
I think you mean selection = things 
> inject({}) do |h,th|
> h[th.name] = h.has_key? th.name ? nil : th
When I tried your code I found you need parens:
h[th.name] = h.has_key?(th.name) ? nil : th
Otherwise it's like:
h[th.name] = h.has_key? (th.name ? nil : th)
and you get [false, false, false, ...].
unknown wrote:
Sorry -- s/Miles/Milo/
No problem - it happens all the time.
selection = things.inject({}) {|h,th| h[th.name] ||= th; h}.values
...looks like what I was after, thanks, allowing everything to be done
in one line. I had not heard of inject, so that is generally useful to
know.
···
--
Posted via http://www.ruby-forum.com/\.