1

I've never used List Comprehensions but have spent the last few hours trying to apply what I've read to a method I'm working on. It's difficult for me to apply the examples I've seen to my case.

Given a list, create a new list of equal size. In that new list, shift all the numbers to the left with zeroes to the right. So for example, [0, 3, 0, 4] will return [3, 4, 0, 0]. I have a working method that does this:

def merge(self, line):
    results = [0] * len(line)
    count = 0
    for number in line:
        if number != 0:
            results[count] = number
            count += 1
    return results

Each time I try my hand at compressing that, I get stumped by how to accomplish results[count] = number without an index.

Vin Breau
  • 269
  • 3
  • 17

5 Answers5

2

You could do:

def merge(self, line):
    return ([x for x in line if x] + ([0] * len(line)))[:len(line)]

This gets the non-zero items from line then pads with zeros, slicing to ensure appropriate length.

As you aren't using any class or instance parameters, this could be made a static method to remove the unnecessary argument:

@staticmethod
def merge(line):
    return ([x for x in line if x] + ([0] * len(line)))[:len(line)]

However, note that your current version is clear, readable and more memory-efficient than this (which builds two lists, adds them to make a third then slices out a fourth).

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • This method is within a class. I'm unfamiliar with static methods or decorations. I don't quite grasp `[x for x in line if x]`. Shouldn't it be instead `[ x for x in l if x != 0]`? – Vin Breau Jun 12 '14 at 23:31
  • @VinBreau `0` evaluates `False` in a Boolean context (and non-zero evaluates `True`), so you don't need `!= 0`. – jonrsharpe Jun 12 '14 at 23:39
2

try:

l = [0, 3, 0, 4]
nl = [ x for x in l if x != 0]
print nl.extend([0] * (len(l) - len(nl))
felixc
  • 168
  • 2
  • 11
2

Try the following:

def merge(self, line):
    return [item for item in line if item != 0]+[0 for item in line if item == 0]

Which runs as:

>>> merge([0, 3, 0, 4])
[3, 4, 0, 0]
>>> 
A.J. Uppal
  • 19,117
  • 6
  • 45
  • 76
0

In your case, just sort the list by boolean key:

def merge(self, line):
    return sorted(line, key=lambda x: not bool(x))

Yes, this solution contains no list comprehension. But not every problem is solved with a hammer.

Daniel
  • 42,087
  • 4
  • 55
  • 81
  • This is neat, but relies on `sorted` being stable (CPython's timsort is, but I'd count that as an implementation detail). – jonrsharpe Jun 12 '14 at 23:06
  • I'm specifically trying to grasp list comprehensions today. Thanks for showing me something new I wasn't aware of though. – Vin Breau Jun 12 '14 at 23:38
0

It's messy, but here it is in one comprehension:

l1 = [0, 3, 0, 4]
l2 = [i for i in filter(bool, l1) + list(repeat(0, len(l1) - len(filter(bool, l1))))]
print l2  # prints '[3, 4, 0, 0]'

You might as well not even include the comprehension because it's redundant:

l1 = [0, 3, 0, 4]
l2 = filter(bool, l1) + list(repeat(0, len(l1) - len(filter(bool, l1))))
print l2  # prints '[3, 4, 0, 0]'

Furthermore, you should probably use some kind of state for the calculation to avoid duplicating work:

l1 = [0, 3, 0, 4]
l2 = filter(bool, l1)
l3 = l2 + list(repeat(0, len(l1) - len(l2)))
print l3  # prints '[3, 4, 0, 0]'
David Sanders
  • 4,069
  • 1
  • 24
  • 38