Tuesday 1 June 2010

5 things you can do with a Python list in one line

This is directly inspired by an excellent post by Drew Olson 5 things you can do with a Ruby array in one line. When reading it, I couldn't help but thinking of the Python versions (and how I like them more :>). So here it is:

  1. Summing elements
    puts my_array.inject(0){|sum,item| sum + item}
    sum(my_list)
  2. Double every item.
    my_array.map{|item| item*2 }
    [2 * x for x in my_list]
  3. Finding all items that meet your criteria.
    my_array.find_all{|item| item % 3 == 0 }
    [x for x in my_list if x % 3 == 0]
  4. Combine techniques.
    my_array.find_all{|item| item % 3 == 0 }.inject(0){|sum,item| sum + item }
    sum(x for x in my_list if x % 3 == 0)
  5. Sorting.
    my_array.sort
    my_array.sort_by{|item| item*-1}
    sorted(my_list)
    sorted(my_list, reverse=True)

13 comments:

Tartley said...

If 'sum' didn't exist as a special case, then maybe it's slightly more instructive to see how you would construct it, using:

from operator import add
reduce(add, mylist)

Although I guess this isn't one line any more

Is there a nice way to markup code in the comments?

Konrad said...

I don't think so.

Migrating away from blogger is on my somday/maybe list, so in when it happens in distant future, I'll make sure to enable some comment markup :)

Robin Norwood said...

Without importing a function, and on one line, Tartley's code would look something like:

reduce(lambda x, y: x + y, mylist)

Brandon Rhodes said...

Nice post! I like the Python versions better as well. :-)

Unknown said...

Now I see why I picked Python instead of Ruby!

Dimitris Leventeas said...

The 4th example (combination of techniques) is not an actual list but a generator. As each element is produced, it is accumulated by applying the addition. That's why is so cheap in terms of memory. Even better than a list.

Unknown said...

Some cool things you can also do:

>>> list = [1,2,3,4,5,6,7,8,9]
>>> list = list[::2]
list is: [1, 3, 5, 7, 9]
(every-other outside)

>>> list = list[1::2]
list is: [2, 4, 6, 8]
(every-other inside)

>>> list = list[::-1]
list is: [9, 8, 7, 6, 5, 4, 3, 2, 1]
(reverse list)

>>> print list[::-2]
list is: [9, 7, 5, 3, 1]
(reverse every-other outside)

I stumbled on this when I thought: I remember seeing an example with 2 colons, I wonder what it will do?

Roberto Alsina said...

I wonder what happens if you use that ruby code to sort a list of strings ( the python version works of course ;-)

Michael Pratt said...

You forgot the bonus!

my_array.find_all{|item| item =~ /\d{3}-\d{3}-\d{4}/ }
[x for x in l if re.match(r'\d{3}-\d{3}-\d{4}', x)]

Aaron said...

Just wanted to let you know that I loved your post and I continued your trend, expanding the coverage to Perl 6. Thanks for doing this!

michael said...

For those of you comparing Ruby and Python, let me fix your ruby:

Summing elements:

my_array.inject(&:+)

sum(my_list)


Double every item.

my_array.map{|x| x*2 }

[2 * x for x in my_list]


Finding all items that meet your criteria.

my_array.select{|x| x % 3 == 0 }

[x for x in my_list if x % 3 == 0]


Combine techniques.

my_array.select{|x| x % 3 == 0 }.inject(&:+)

sum(x for x in my_list if x % 3 == 0)


Sorting.

my_array.sort

my_array.sort.reverse

sorted(my_list)

sorted(my_list, reverse=True)


Matching by regexp (@Michael Pratt)

my_array.grep /\d{3}-\d{3}-\d{4}/

[x for x in l if re.match(r'\d{3}-\d{3}-\d{4}', x)]

Konrad said...

@michael
My intention wasn't to compare the number of characters, but the way you express it. I kept the original var names, and wrote the Python version as I would write it myself (I believe single letter variable names are preferable for list comprehensions).

When it comes to the first example, this is not valid ruby, actually. You need one of the Rails' gems (ActiveSupport, isn't it?) to implement to_proc on symbol.

Aaron said...

I know this is several months later, but using a symbol for reduce is supported in the Ruby core.

http://www.ruby-doc.org/core/classes/Enumerable.html#M001495