4

I have a list of lists like this: [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]].

I want to write a function that will return: [16, 14, 12, 7, 6]: i.e. the last 5 elements in the list of lists.

This is the code I have, but it is not very pythonic at all (master_list contains the list above):

    def find_last_five():
        last_five = []
        limit = 5

        for sublist in reversed(master_list):
            # have to check that list is not None.
            if sublist:
                for elem in sublist:
                    last_five.append(elem)
                    limit -= 1
                    if (limit == 0):
                         return last_five

        return last_five
Atul Bhatia
  • 1,633
  • 5
  • 25
  • 50

7 Answers7

4

Given your example; I will assume your items in your list are either an iterable or None;

>>> import itertools

>>> lst = [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
>>> print list(itertools.chain(*[l for l in lst if l is not None]))[-5:]
[6, 7, 12, 14, 16]
Ozgur Vatansever
  • 49,246
  • 17
  • 84
  • 119
4
import itertools as it

a = [[1, 2], [4, 5, 6], [], [7, 12, 14, 16]]
reversed(it.islice(it.chain.from_iterable(reversed(a)), 5))

That actually assumes there are no None's in a. If there are just do a = filter(a, None).

U2EF1
  • 12,907
  • 3
  • 35
  • 37
4

You can use a list comprehension:

>>> tgt=[[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
>>> [e for sub in tgt if sub for e in sub][-5:]
[6, 7, 12, 14, 16]

That filters out the None. To filter out other non-list or tuples:

>>> [e for sub in tgt if isinstance(sub, (list, tuple)) for e in sub][-5:]

If you want something that does not have to flatten the entire list of lists first, you can just deal with the structure from the end and move up until you have what you want:

result=[]
current=[]
it=reversed(tgt)
while len(result)<5:
    if current:
        result.append(current.pop())
        continue
    else:
        try: 
             current=next(it)
        except StopIteration:
            break

(Or use John 1024's solution)

Community
  • 1
  • 1
dawg
  • 98,345
  • 23
  • 131
  • 206
  • This solution actually seems the clearest to me. Thanks. Didn't realize you could chain the for x in y statements like that. – Atul Bhatia Feb 19 '15 at 05:57
  • 1
    @AtulBhatia: It's clear and compact, but if the total number of elements is much larger than 5 then this approach is not so efficient, since it has to process all the sublists in `tgt`. John1024's and U2EF1's solutions stop as soon as they've reached the desired total. – PM 2Ring Feb 19 '15 at 06:46
  • is that true? In U2EF1's solution we have to filter out the None's beforehand as well. – Atul Bhatia Feb 19 '15 at 07:36
3

Using no external modules:

master = [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
new = []
total = 5

for x in reversed(master):
    if x:
        new += list(reversed(x))[:total-len(new)]
        if total == len(new):
            break

print(new)

This produces:

[16, 14, 12, 7, 6]

which is the desired list with the elements in the desired order.

John1024
  • 109,961
  • 14
  • 137
  • 171
  • While not as compact as some of the other solutions, this one is clear and easy to understand. And it's certainly a great approach if the number of elements in the sublists is much larger than the desired total. – PM 2Ring Feb 19 '15 at 06:47
0

Alternative approach using flatten recipe:

import collections

l = [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]    

def flatten(l):

    for el in l:
        if isinstance(el, collections.Iterable) and not isinstance(el, str):
            for sub in flatten(el):
                yield sub
        else:
            yield el


print([v for v in flatten(l) if v][-5:])     
# gives: [6, 7, 12, 14, 16]
Community
  • 1
  • 1
Marcin
  • 215,873
  • 14
  • 235
  • 294
0

How about a different approach?

a = [[1, 2], [4, 5, 6], [], None, [7, 12, 14, 16]]
sum(filter(None, a), [])[-1:-6:-1]

The filter function is necessary only because of the None type in the list. In case it is just a list of lists, this will be lot simpler to write like this:

sum(a, [])[-1:-6:-1]

The principle behind this? We actually use the '+' operator of list to just keep on adding the lists into a single list. Please note that this is not the way to choose(if you choose ;)) for longer lists. For smaller and medium lists, this is fine.

thiruvenkadam
  • 4,170
  • 4
  • 27
  • 26
-1

I'd use itertools to do this. Something like

list(itertools.chain.from_iterable(x for x in l if x is not None))[:-5]

where l is your input list.

Noufal Ibrahim
  • 71,383
  • 13
  • 135
  • 169