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

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

Thanks,
Brad

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

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.

···

On 11/25/06, Brad Tilley <rtilley@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]

--
Lou.

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

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

Thanks,
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

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

···

On 2006-11-25 18:47:26 -0500, Brad Tilley <rtilley@vt.edu> said:

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

Quoting Mike Austin <noone@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.

Hi --

···

On Mon, 27 Nov 2006, James Cunningham wrote:

On 2006-11-25 18:47:26 -0500, Brad Tilley <rtilley@vt.edu> said:

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

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

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

--
                   David A. Black | dblack@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] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

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

···

On 11/27/06, James Cunningham <jameshcunningham@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 }

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

···

On 11/26/06, Mike Austin <noone@nowhere.com> wrote:

Brad Tilley wrote:
<snip>
Because Ruby's select() returns a value, not just a boolean like in
Smalltalk,

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

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

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 17:52:37 -0500, dblack@wobblini.net said:

Hi --

On Mon, 27 Nov 2006, James Cunningham wrote:

On 2006-11-25 18:47:26 -0500, Brad Tilley <rtilley@vt.edu> said:

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

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

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

The same.

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

Best,
James

···

On 2006-11-27 23:39:57 -0500, "Phrogz" <gavin@refinery.com> said:

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 28.11.2006 05:39, Phrogz wrote:

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

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

Best,
James

···

On 2006-11-26 19:01:53 -0500, James Cunningham <jameshcunningham@gmail.com> said:

On 2006-11-26 17:52:37 -0500, dblack@wobblini.net said:

Hi --

On Mon, 27 Nov 2006, James Cunningham wrote:

On 2006-11-25 18:47:26 -0500, Brad Tilley <rtilley@vt.edu> said:

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

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

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

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

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

···

On 27.11.2006 01:01, James Cunningham wrote:

On 2006-11-26 17:52:37 -0500, dblack@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]

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?

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

···

On 2006-11-27 03:30:58 -0500, Robert Klemme <shortcutter@googlemail.com> said:

On 27.11.2006 01:01, James Cunningham wrote:

On 2006-11-26 17:52:37 -0500, dblack@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 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: