Ruby and list comprehension

In Python, I can do this to arrays:

added = [x for x in new_data if x not in old_data]
removed = [x for x in old_data if x not in new_data]
same = [x for x in new_data if x in old_data]

I believe this is known as list comprehension in Python. How is this
done in
Ruby?

Thanks,
Brad

Brad T. wrote:

Brad

This question comes up from time to time. You can search the archives of
comp.lang.ruby on Google G. for “list comprehension” to find the
previous threads on this topic.

http://groups.google.com/group/comp.lang.ruby/search?group=comp.lang.ruby&q=list+comprehension&qt_g=1&searchnow=Search+this+group

On 11/25/06, Brad T. [email protected] wrote:

In Python, I can do this to arrays:

added = [x for x in new_data if x not in old_data]
removed = [x for x in old_data if x not in new_data]
same = [x for x in new_data if x in old_data]

Short answer:

added   = new_data.reject {|i| old_data.include? i }
removed = old_data.reject {|i| new_data.include? i }
same    = new_data.select {|i| old_data.include? i }

Provided ordering isn’t important here, you can do the same thing with
set operations.

require 'set'

old_data = old_data.to_set
new_data = new_data.to_set

added   = new_data - old_data
removed = old_data - new_data
same    = new_data.intersection(old_data)

Note those returns sets, not arrays.

Brad T. wrote:

Brad
Because Ruby’s select() returns a value, not just a boolean like in
Smalltalk,
you can do the following:

[1,2,3].select { |x| ![2,3,4].include? x }
[2,3,4].select { |x| ![1,2,3].include? x }
[1,2,3].select { |x| [2,3,4].include? x }

You can also use Array operators:

[1,2,3] - [2,3,4]
[2,3,4] - [1,2,3]
[1,2,3] & [2,3,4]

Mike

On 2006-11-25 18:47:26 -0500, Brad T. [email protected] said:

Brad
I found this while web searching for the same thing recently; I can’t
recall where I found it. It’s a cute little hack.

class Array
def comprehend
return self unless block_given?
result = []
self.each { |i| result.push yield(i) }
result.compact
end
end

Then:

added = new_data.comprehend { |x| x if not old_data.include? x }
removed = old_data.comprehend { |x| x if not new_data.include? x }
same = new_data.comprehend { |x| x if old_data.include? x }

Best,
James

Quoting Mike A. [email protected]:

[1,2,3] - [2,3,4]
[2,3,4] - [1,2,3]
[1,2,3] & [2,3,4]

Thanks for all the examples guys! That’s great stuff.

Hi –

On Mon, 27 Nov 2006, James C. wrote:

Ruby?
result = []
self.each { |i| result.push yield(i) }
result.compact
end
end

Then:

added = new_data.comprehend { |x| x if not old_data.include? x }
removed = old_data.comprehend { |x| x if not new_data.include? x }
same = new_data.comprehend { |x| x if old_data.include? x }

I’m not getting how that’s better than:

added = new_data.select {|x| not old_data.include?(x) }

(or the reject equivalent) and so on.

David

On 2006-11-26 17:52:37 -0500, [email protected] said:

same = [x for x in new_data if x in old_data]
class Array
added = new_data.comprehend { |x| x if not old_data.include? x }
David
I should have clarified. In your example there’s no difference, but the
above gives a general replacement for list comprehensions.

irb(main):018:0> (1…25).to_a.comprehend { |x| x**2 if not x % 2 == 0 }
=> [1, 9, 25, 49, 81, 121, 169, 225, 289, 361, 441, 529, 625]

Best,
James

On 2006-11-26 19:01:53 -0500, James C.
[email protected] said:

added = [x for x in new_data if x not in old_data]
recall where I found it. It’s a cute little hack.
Then:

Best,
James

Er, “your” meaning “Brad T.'s”.

Best,
James

On 11/27/06, James C. [email protected] wrote:

added = new_data.comprehend { |x| x if not old_data.include? x }
removed = old_data.comprehend { |x| x if not new_data.include? x }
same = new_data.comprehend { |x| x if old_data.include? x }

I wrote http://zem.novylen.net/ruby/fproduct.rb a while ago to emulate
list comprehensions in ruby - for example:

for x,y,z in product 1…40,
1…40, proc {|x,y| x <= y},
1…40, proc {|x,y,z| x2 + y2 == z**2}
p [x,y,z]
end

The benefit is that the filters do get applied in order (sorted on
arity), so that it doesn’t generate all the combinations first and
then filter.

martin

I’ve always thought list comprehension is just a bunch of
map/filter/… transformation until I saw the following version of
permutation:

in Haskell:
permutation [] = [[]]
permutation xs = [x:ys | x <- xs, ys <- permutation (delete x xs)]

in Erlang:
permutation([]) -> [[]];
permutation(L) -> [[H|T] || H <- L, T <- permutation(L–[H])].

really neat, isn’t it?

On 11/26/06, Mike A. [email protected] wrote:

Brad T. wrote:

Because Ruby’s select() returns a value, not just a boolean like in
Smalltalk,

OT of course :wink:
… this does not seem true for all Smalltalks, I am using Squeak and
select: returns the filtered array, I am quite surprised that it worked
differently before.

Robert


The reasonable man adapts himself to the world; the unreasonable one
persists in trying to adapt the world to himself. Therefore all progress
depends on the unreasonable man.

  • George Bernard Shaw

On 2006-11-27 03:30:58 -0500, Robert K. [email protected]
said:

irb(main):003:0> (1…25).select {|x| x % 2 == 1}.map! {|x| x**2}
=> [1, 9, 25, 49, 81, 121, 169, 225, 289, 361, 441, 529, 625]

Kind regards

robert

That’s fair enough, but I think list comprehension is clearer than
method chaining and one-liner array accumulation. I’m afraid

a << x**2 if x % 2 == 1; a

is perhaps just a little less elegant than

x**2 if x % 2 == 1

I think the nicest thing about Ruby, though, is that it’s even possible.

Best,
James

piggybox wrote:

in Haskell:
permutation [] = [[]]
permutation xs = [x:ys | x <- xs, ys <- permutation (delete x xs)]

in Erlang:
permutation([]) -> [[]];
permutation(L) -> [[H|T] || H <- L, T <- permutation(L–[H])].

really neat, isn’t it?
Yes. :slight_smile:

On 27.11.2006 01:01, James C. wrote:

On 2006-11-26 17:52:37 -0500, [email protected] said:

I’m not getting how that’s better than:

added = new_data.select {|x| not old_data.include?(x) }

(or the reject equivalent) and so on.

I should have clarified. In your example there’s no difference, but the
above gives a general replacement for list comprehensions.

irb(main):018:0> (1…25).to_a.comprehend { |x| x**2 if not x % 2 == 0 }
=> [1, 9, 25, 49, 81, 121, 169, 225, 289, 361, 441, 529, 625]

Frankly, I am not sure I find this better than using the built in
methods:

irb(main):001:0> (1…25).inject([]) {|a,x| a << x**2 unless x % 2 == 0;
a}
=> [1, 9, 25, 49, 81, 121, 169, 225, 289, 361, 441, 529, 625]

irb(main):002:0> (1…25).inject([]) {|a,x| a << x**2 if x % 2 == 1; a}
=> [1, 9, 25, 49, 81, 121, 169, 225, 289, 361, 441, 529, 625]

irb(main):003:0> (1…25).select {|x| x % 2 == 1}.map! {|x| x**2}
=> [1, 9, 25, 49, 81, 121, 169, 225, 289, 361, 441, 529, 625]

Kind regards

robert

James C. wrote:

end
Maybe I don’t comprehend comprehending, but why the result/each instead
of map?

class Array
def comprehend
if block_given?
map{ |i| yield( i ) }.compact
else
self
end
end
end

or perhaps better

class Array
def comprehend( &block )
block ? map( &block ).compact : self
end
end

On 28.11.2006 05:39, Phrogz wrote:

class Array
def comprehend( &block )
block ? map( &block ).compact : self
end
end

This could go into Enumerable instead. There is no special Array
functionality involved.

Kind regards

robert

On 2006-11-27 23:39:57 -0500, “Phrogz” [email protected] said:

class Array
def comprehend( &block )
block ? map( &block ).compact : self
end
end

The same.

You’re just cleverer than I am. :wink:

Best,
James