python get last 5 elements in list of lists - python

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

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

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]

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)

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.

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]

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.

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.

Related

Fastest way to split a list into a list of lists based on another list of lists

Say I have a list that contains 5 unique integers in the range of 0 to 9.
import random
lst = random.sample(range(10), 5)
I also have a list of lists, which is obtained by splitting integers from 0 to 19 into 6 groups:
partitions = [[8, 12], [2, 4, 16, 19], [1, 6, 7, 13, 14, 17], [3, 15, 18], [5, 9, 10, 11], [0]]
Now I want to split lst based on the reference partitions.
For example, if I have
lst = [0, 1, 6, 8, 9]
I expect the output to be a list of lists like this:
res = [[0], [1, 6], [8], [9]]
I want the algorithm to be as fast as possible. Any suggestions?
res=[]
for sublist in partitions: # go through all sublists in partitions
match = [i for i in lst if i in sublist] # find matching numbers in sublist and lst
if match: # if it is empty don't append it to res
res.append(match)
# at this point res is [[8], [1, 6], [9], [0]]
print(sorted(res)) # use sorted to get desired output
I don't know if this is the fastest algorithm but it works
import random
lst = random.sample(range(10), 5)
partitions = [[8, 12], [2, 4, 16, 19], [1, 6, 7, 13, 14, 17], [3, 15, 18], [5, 9, 10, 11], [0]]
sequence = []
result = []
for i in range(5):
for j in range(len(partitions)):
if lst[i] in partitions[j]:
if j in sequence:
where = sequence.index(j)
result[where] += [lst[i]]
else:
result += [[lst[i]]]
sequence += [j]
break
print(result)

List of lists python: combine list elements that are the same size

I have a list of lists in python:
[[1],[2],[3,4],[5,6],[7,8,9,10,11],[12,13,14,15,16],[17]]
I would like to combine the sublists into a single sublist if they hold the same number of elements:
[[1,2,17],[3,4,5,6],[7,8,9,10,11,12,13,14,15,16]]
Is there a simple way of doing this?
Use groupby and chain from itertools
Ex:
from itertools import groupby, chain
lst = [[1],[2],[3,4],[5,6],[7,8,9,10,11],[12,13,14,15,16],[17]]
result = [list(chain.from_iterable(v)) for k, v in groupby(sorted(lst, key=lambda h: len(h)), lambda x: len(x))]
print(result)
Output:
[[1, 2, 17], [3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16]]
sorted(lst, key=lambda h: len(h)) to sort your list by len
then use groupby to group your list by len of list
A "simpler" approach without itertools:
dictByLength = {}
for i in mylist:
dictByLength[len(i)] = dictByLength.get(len(i), []) + i
print(list(dictByLength.values()))
output:
[[1, 2, 17], [3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16]]
Here is my approach (without using itertools):
l = [[1],[2],[3,4],[5,6],[7,8,9,10,11],[12,13,14,15,16],[17]]
# create one sublist for each possible length
m = [[] for size in range(len(max(l, key=len)))]
# append to each size-sublist, the appropriate sublist
for sub_l in l:
size = len(sub_l)
m[size - 1] += sub_l
# remove empty sub lists
m = [sub_m for sub_m in m if sub_m]
print(m)
[[1, 2, 17], [3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16]]

Absolute value from nested list using recursion

I have this nested list:
list1 = [2,-6, [8,-12,-12, [4, [-6], -3]], 7, [3.55, -3.55]].
And I have to use recursion to get absolute values of all elements from the lists so output stays as a list:
[2, 6, [8, 12, 12, [4, [6], 3]], 7, [3.55, 3.55]].
This is the code:
def rec_abs(a):
new_list=[]
for el in a:
if isinstance(el, list):
new_list.append(rek_abs(el))
else:
new_list.append(el)
return new_list
print(rek_abs([2,-6, [8,-12,-12, [4, [-6], -3]], 7, [3.55, -3.55]]))
Can You give me some tips to solve it (I dont expect full solultion, just some tips)?
Thank You a lot!
Everything is perfect, just use abs() to convert it into absolute value
Note : You have typo in code as rec_abs and rek_abs, which I modified it in below code
def rec_abs(a):
new_list=[]
for el in a:
if isinstance(el, list):
new_list.append(rec_abs(el))
else:
new_list.append(abs(el))
return new_list
print(rec_abs([2,-6, [8,-12,-12, [4, [-6], -3]], 7, [3.55, -3.55]]))
[2, 6, [8, 12, 12, [4, [6], 3]], 7, [3.55, 3.55]]
For reference, modifying the list in-place is a simple, more efficient alternative, so you should consider this whenever possible. When iterating, iterate over the indices, and assign the return value to the ith index.
def rec_abs(a):
for i, el in enumerate(a):
a[i] = rec_abs(el) if isinstance(el, list) else abs(a[i])
return a
lst = [2,-6, [8,-12,-12, [4, [-6], -3]], 7, [3.55, -3.55]]
print(rec_abs(lst))
[2, 6, [8, 12, 12, [4, [6], 3]], 7, [3.55, 3.55]]

how to merge two sublists sharing any number in common?(2)

Given that:
list=[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11]]
I have asked a similar question before, I have tried the code on
how to merge two sublists sharing any number in common?
but I am stuck in my code now.
I want to merge the sublists that share a common number,
e.g. [1,2,3] and [3,4,5] can merge to give [1,2,3,4,5] as they share a common number, 3.
In [[1,2,3],[3,4,5],[5,6]], although [1,2,3] and [3,4,5] share a common number, 3,
[3,4,5] and [5,6] also share a common number, 5, so I want all three of them to merge then gives
[1,2,3,4,5,6].
So for list,
my expected result is:
[[1,2,3,4,5,6,7],[9,10,11]]
I have tried the following code but don't know what is wrong, can anyone help?
s = map(set, list)
for i, si in enumerate(s):
for j, sj in enumerate(s):
if i != j and si & sj:
s[i] = si | sj
s[j] = set()
list=[list(el) for el in s if el]
print list
>>>[[5, 6, 7], [9, 10, 11]]
def merge_containing(input_list):
merged_list = [set(input_list[0])]
i = 0
for sub_list in input_list:
if not set(sub_list).intersection(set(merged_list[i])): # 1
merged_list.append(set()) # 2
i += 1 # 2
merged_list[i].update(set(sub_list)) # 3
return [sorted(sub_list) for sub_list in merged_list] # 4
mylist=[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11]]
print merge_containing(mylist)
Output:
[[1, 2, 3, 4, 5, 6, 7], [9, 10, 11]]
How does it work:
Check if the sub_list set shares any common member with the current
index set of the merged_list.
If it doesn't, add a new empty set to the merged_list and increment
the index.
Adds the sub_list set to the set at index in the merged_list.
Converts from set to list and return
def merge(list_of_lists):
number_set = set()
for l in list_of_lists:
for item in l:
number_set.add(item)
return sorted(number_set)
if __name__ == '__main__':
list_of_lists = [[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11]]
merged = merge(list_of_lists)
print merged
I'm posting this as a new answer since the OP already accepted my other.
But as pointed out by #Eithos,
the input:
[[3,4], [1,2], [1,3]]
should return
[[1,2,3,4]]
and the input
[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11],[65,231,140], [13,14,51]]
should return
[[1, 2, 3, 4, 5, 6, 7], [9, 10, 11], [13, 14], [51], [65], [140], [231]]
Here's my attempt:
from itertools import chain
def merge_containing(input_list):
print " input:", input_list
chain_list = sorted(set(chain(*input_list))) # 1
print " chain:",chain_list
return_list = []
new_sub_list = []
for i, num in enumerate(chain_list):
try:
if chain_list[i + 1] == chain_list[i] + 1: # 2
new_sub_list.append(num)
else:
new_sub_list.append(num) # 3
return_list.append(new_sub_list)
new_sub_list = []
except IndexError:
new_sub_list.append(num) # 3
return_list.append(new_sub_list)
print 'result:', return_list
return return_list
mylist = [[3,4], [1,2], [1,3]]
merge_containing(mylist)
print
mylist = [[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11],[65,231,140], [13,14,51]]
merge_containing(mylist)
Output:
input: [[3, 4], [1, 2], [1, 3]]
chain: [1, 2, 3, 4]
result: [[1, 2, 3, 4]]
input: [[1, 2, 3], [3, 4, 5], [5, 6], [6, 7], [9, 10], [10, 11], [65, 231, 140], [13, 14, 51]]
chain: [1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 13, 14, 51, 65, 140, 231]
result: [[1, 2, 3, 4, 5, 6, 7], [9, 10, 11], [13, 14], [51], [65], [140], [231]]
Explanation:
This one is a little hacky them the last one
I use itertool.chain to flat all the lists and them I sort it.
Then I check if the current number is within the range of 1 digit from the next
If it is I store it in the new_sub_list, if not I store in the new_sub_list, then store new_sub_list in the return_list, and empty the new_sub_list.
Note the try/except Index Error, it to avoid comparing the last item of the list with one that doesn't exist,
Well... I couldn't resist answering #f.rodrigues' last answer with one of my own.
I have to be honest though, this final version was heavily influenced by jonrsharpe's solution (the code went through various revisions, each one more efficient until I realized his method was the only way to press the most amount of juice) over here: Using sublists to create new lists where numbers don't repeat
Which made me wonder... why are we answering the same question over and over again (from the very same person)? This question, how to merge two sublists sharing any number in common? and the one with jonrsharpe's solution.
Anyway, this joins lists in the way outlined in his first question, but, like the solutions he already received over there, this one also works just as well for solving this problem.
sequence = [[1, 4, 9], [2, 3, 6], [4, 13, 50], [13, 23, 29], [2, 3, 7]]
def combineSequences(seq):
for index, y in enumerate(seq):
while True:
for x in seq[index + 1:]:
if any(i in x for i in seq[index]):
seq.remove(x)
y.extend(x)
break
else:
index += 1
break
return [sorted(set(l)) for l in seq]
sequence = [[1, 4, 9], [2, 3, 6], [4, 13, 50], [13, 23, 29], [2, 3, 7]]
print combineSequences(sequence)
>>> [[1, 4, 9, 13, 23, 29, 50], [2, 3, 6, 7]]
sequence = [[3, 4], [1, 2], [1, 3]]
print combineSequences(sequence)
>>> [[1, 2, 3, 4]]
This solution operates under a different assumption than the one I made earlier, just to clarify. This simply joins lists that have a common number. If the idea, however, was to only have them separated by intervals of 1, see my other answer.
That's it!
Okay. This solution may be difficult to grasp at first, but it's quite logical and succint, IMO.
The list comprehension basically has two layers. The outer layer will itself create separate lists
for each value of i (outer index) that satisfies the condition that the value it points to in the list is not equal to the value pointed to by the previous index + 1. So every numerical jump greater than one will create a new list within the outer list comprehension.
The math around the second (inner list comprehension) condition is a bit more complicated to explain, but essentially the condition seeks to make sure that the inner list only begins counting from the point where the outer index is at, stopping to where once again there is a numerical jump greater than one.
Assuming an even more complicated input:
listVar=[[1,2,3],[3,4,5],[5,6],[6,7],[9,10],[10,11],[65,231,140], [13,14,51]]
# Flattens the lists into one sorted list with no duplicates.
l = sorted(set([x for b in listVar for x in b]))
lGen = xrange(len(l))
print [
[l[i2] for i2 in lGen if l[i2] + i == l[i] + i2]
for i in lGen if l[i] != l[i-1] + 1
]
>>> [[1, 2, 3, 4, 5, 6, 7], [9, 10, 11], [13, 14], [51], [65], [140], [231]]

function that sums up nested list values?

So im attempting to create a recursion function where it takes each item in the list and sums it up altogether, now I know theres a simple built in function sum(a) but I'm trying to work with nested lists such as this below but I keep getting thrown an error.
def sumList():
list2 = [1, [2, 3,[4, 5, 6], 7, [8, [9, 10]], 11]]
newlist = []
lol = 0
for i in range (len(list2)):
if type(list2[i]) == type([]):
print list2[i], "here"
for i in range (len(list2[i])):
lol += (len(list2[i]))
newlist.append(i[:len(i)+1])
if len(list2)==0:
return None
else:
print list2[i]
lol+=list2[i]
print lol
sumList()
Now I know i've got a lot implemented in the program that I imagine isn't needed, but the error I
keep getting is
1
[2, 3, [4, 5, 6], 7, [8, [9, 10]], 11] here
TypeError: object of type 'int' has no len()
In general, you could flatten your list of lists and search for min in the flattened list. There are many recipes for flattening. Below is one that I took from here.
import collections
def flatten(iterable):
for el in iterable:
if isinstance(el, collections.Iterable) and not isinstance(el, str):
yield from flatten(el)
else:
yield el
list2 = [2, 3, [4, 5, 6], 7, [8, [9, 10]], 11]
print(list(flatten(list2)))
# [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
print(sum(flatten(list2)))
# 65
def r_sum(mylist,nsum=0):
for i in mylist:
if isinstance(i,int):
nsum += i
else:
nsum += r_sum(i)
return nsum
# Python 2.7
def recursiveSum(data):
# This naively assumes that if it's not an int, it's a list
# You may want to add more error handling if you expect dirtier data
if isinstance(data, int): return data
mySum = 0
for i in data: mySum += recursiveSum(i)
return mySum
list2 = [1, [2, 3,[4, 5, 6], 7, [8, [9, 10]], 11]]
print recursiveSum(list2) # Should get 66

Categories

Resources