AlexC
AlexC

Reputation: 3914

What is the Pythonic way to get last not-None value from a list

The following code gets the last present value from an ordered list of dated values:

def test_get_last_value(self):
    dated_optional_values = [(datetime.datetime(2015, 2, 23), None),
                    (datetime.datetime(2015, 2, 24), 1.23),
                    (datetime.datetime(2015, 2, 25), None),
                    (datetime.datetime(2015, 2, 26), 2.34),
                    (datetime.datetime(2015, 2, 27), None),
                    ]
    present_values = [dated_optional_value[1] for dated_optional_value in dated_optional_values
        if dated_optional_value[1] is not None]
    last_present_value = present_values[-1]
    print('last_present_value=', last_present_value)

I can easily write a loop which starts at the end of the list and iterates back until it finds a value.

Is there a more Pythonic way to accomplish that?

Upvotes: 2

Views: 3787

Answers (4)

6502
6502

Reputation: 114569

It may be nice to factor the separate steps:

  1. You want the second column (x[1])

    def column(data, index): return [x[index] for x in data]

  2. You want to filter out None

    def notNone(data): return [x for x in data if x is not None]

  3. You want the last element

    def last(data): return data[-1]

Then your problem becomes

last_preset_value = last(notNone(column(data, 1)))

Upvotes: 1

Jon Clements
Jon Clements

Reputation: 142206

You can also generalise this to take the last n non None values - for instance, to take the last 2 non blanks:

from itertools import islice

candidates = (el[1] for el in reversed(dated_optional_values) if el[1] is not None)
last_n = list(islice(candidates, 2)) or []

Also, if you don't have a Sized container, such as a list, and have some arbitrary iterable, you can use a collections.deque with a fixed size, eg:

from collections import deque

candidates = (el[1] for el in dated_optional_values if el[1] is not None)
d = deque([], 2)
d.extendleft(candidates)
matches = list(d) or []

Both of these options return:

[2.34, 1.23]

Upvotes: 0

Steve Jessop
Steve Jessop

Reputation: 279325

next((dop[1] for dop in reversed(date_optional_values) if dop[1] is not None), None)

You can replace the final None with whatever other value you prefer to return the case where every value is None. Or omit that argument to throw an exception in the case where they're all None.

Upvotes: 3

kindall
kindall

Reputation: 184280

Maybe:

last_present_value = next(x for x in reversed(dated_optional_values) 
                          if x[1] is not None)

Upvotes: 3

Related Questions