# Ruby and list comprehension

**URL:** <https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282>\
**Category:** Ruby\
**Created:** [November 26, 2006, 12:48am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282 "2006-11-26T00:48:00Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 26, 2006, 12:48am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/1 "2006-11-26T00:48:00Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 26, 2006, 12:54am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/2 "2006-11-26T00:54:01Z")

</div>

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](http://groups.google.com/group/comp.lang.ruby/search?group=comp.lang.ruby&q=list+comprehension&qt_g=1&searchnow=Search+this+group)

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 26, 2006, 1:08am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/3 "2006-11-26T01:08:02Z")

</div>

On 11/25/06, Brad T. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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.

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 26, 2006, 1:22am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/4 "2006-11-26T01:22:03Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 26, 2006, 11:46pm UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/5 "2006-11-26T23:46:02Z")

</div>

On 2006-11-25 18:47:26 -0500, Brad T. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 26, 2006, 1:30am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/6 "2006-11-26T01:30:04Z")

</div>

Quoting Mike A. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 26, 2006, 11:54pm UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/7 "2006-11-26T23:54:03Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 27, 2006, 1:06am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/8 "2006-11-27T01:06:13Z")

</div>

On 2006-11-26 17:52:37 -0500, removed\_email\_address@domain.invalid 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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 27, 2006, 1:06am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/9 "2006-11-27T01:06:13Z")

</div>

On 2006-11-26 19:01:53 -0500, James C.  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 27, 2006, 9:10am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/10 "2006-11-27T09:10:53Z")

</div>

On 11/27/06, James C. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) 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](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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 27, 2006, 10:51am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/11 "2006-11-27T10:51:04Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 27, 2006, 11:01am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/12 "2006-11-27T11:01:07Z")

</div>

On 11/26/06, Mike A. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Brad T. wrote:  
>   
> Because Ruby’s select() returns a value, not just a boolean like in  
> Smalltalk,

OT of course 😉  
… 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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 27, 2006, 2:01pm UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/13 "2006-11-27T14:01:31Z")

</div>

On 2006-11-27 03:30:58 -0500, Robert K. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 27, 2006, 3:23pm UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/14 "2006-11-27T15:23:39Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 27, 2006, 9:36am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/15 "2006-11-27T09:36:56Z")

</div>

On 27.11.2006 01:01, James C. wrote:

> On 2006-11-26 17:52:37 -0500, removed\_email\_address@domain.invalid 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
```

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 28, 2006, 5:45am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/16 "2006-11-28T05:45:55Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 28, 2006, 11:36am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/17 "2006-11-28T11:36:37Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![brad7a0](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/53a042/32.png) [@brad7a0](https://www.ruby-forum.com/u/brad7a0)\
**Post date:** [November 28, 2006, 6:15am UTC](https://www.ruby-forum.com/t/ruby-and-list-comprehension/77282/18 "2006-11-28T06:15:58Z")

</div>

On 2006-11-27 23:39:57 -0500, “Phrogz” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) said:

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

The same.

You’re just cleverer than I am. 😉

Best,  
James
