Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

ruby and list comprehension

10 views
Skip to first unread message

Brad Tilley

unread,
Nov 25, 2006, 6:47:26 PM11/25/06
to
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

Timothy Hunter

unread,
Nov 25, 2006, 6:53:00 PM11/25/06
to
This question comes up from time to time. You can search the archives of
comp.lang.ruby on Google Groups 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

Louis J Scoras

unread,
Nov 25, 2006, 7:06:43 PM11/25/06
to
On 11/25/06, Brad Tilley <rti...@vt.edu> 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.


--
Lou.

Mike Austin

unread,
Nov 25, 2006, 7:19:38 PM11/25/06
to

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

Brad Tilley

unread,
Nov 25, 2006, 7:29:37 PM11/25/06
to
Quoting Mike Austin <no...@nowhere.com>:

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

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

James Cunningham

unread,
Nov 26, 2006, 5:42:09 PM11/26/06
to

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

dbl...@wobblini.net

unread,
Nov 26, 2006, 5:52:37 PM11/26/06
to
Hi --

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

--
David A. Black | dbl...@wobblini.net
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

James Cunningham

unread,
Nov 26, 2006, 7:01:53 PM11/26/06
to


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

James Cunningham

unread,
Nov 26, 2006, 7:03:34 PM11/26/06
to
On 2006-11-26 19:01:53 -0500, James Cunningham
<jameshcu...@gmail.com> said:

Er, "your" meaning "Brad Tilley's".

Best,
James

Martin DeMello

unread,
Nov 27, 2006, 3:09:26 AM11/27/06
to
On 11/27/06, James Cunningham <jameshcu...@gmail.com> 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| x**2 + y**2 == 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

Robert Klemme

unread,
Nov 27, 2006, 3:30:58 AM11/27/06
to
On 27.11.2006 01:01, James Cunningham wrote:
> On 2006-11-26 17:52:37 -0500, dbl...@wobblini.net 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

piggybox

unread,
Nov 27, 2006, 4:49:12 AM11/27/06
to
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?

James Cunningham

unread,
Nov 27, 2006, 7:59:17 AM11/27/06
to

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

Devin Mullins

unread,
Nov 27, 2006, 9:21:52 AM11/27/06
to
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. :)

Phrogz

unread,
Nov 27, 2006, 11:39:57 PM11/27/06
to
James Cunningham wrote:
> 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

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

James Cunningham

unread,
Nov 28, 2006, 12:13:05 AM11/28/06
to
On 2006-11-27 23:39:57 -0500, "Phrogz" <ga...@refinery.com> said:

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

The same.

You're just cleverer than I am. ;)

Best,
James

Robert Klemme

unread,
Nov 28, 2006, 5:30:21 AM11/28/06
to
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

0 new messages