[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Collect objects from an array based on one unique parameter

Milo Thurston

8/9/2007 9:25:00 AM

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-...attachment/76/mo...

--
Posted via http://www.ruby-....

12 Answers

Konrad Meyer

8/9/2007 9:31:00 AM

0

On Thursday 09 August 2007 02:24:46 am Milo Thurston wrote:
> 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

> 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/mo...

--
Konrad Meyer <konrad@tylerc.org> http://konrad.sobertil...

Milo Thurston

8/9/2007 9:51:00 AM

0

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-....

Robert Klemme

8/9/2007 10:43:00 AM

0

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.

Why don't you use #select?

selection = things.select {|x| x.name = "foo"}

Did I misunderstand your requirement?

Kind regards

robert

Milo Thurston

8/9/2007 11:00:00 AM

0

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-....

David A. Black

8/9/2007 11:27:00 AM

0

Robert Klemme

8/9/2007 11:43:00 AM

0

2007/8/9, Milo Thurston <knirirr@gmail.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

David A. Black

8/9/2007 11:53:00 AM

0

Milo Thurston

8/9/2007 12:02:00 PM

0

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-....

Robert Klemme

8/9/2007 12:04:00 PM

0

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, ...].

Good catches! Thanks! /me confesses I did not try - just check for syntax. :-}

robert

David A. Black

8/9/2007 12:08:00 PM

0