Nested list sort python 3 - python

I am a python beginner--headbangin'. This is a very basic question and I can't seem to find any straight forward answer, either using google or StackOverFlow.
QUESTION:
I have a nested list:
l = [
[1,4,3,n]
[2,2,4,n]
[3,1,5,n]
]
I want to sort the entire list by the second value smallest to largest. I will end up sorting the list again by the third value... and nth value of each nested list.
HOW WOULD ONE SORT based on the SECOND, THIRD, Nth value?
A key is mentioned, but "key=lambda" is often used and that just confuses me more.
EDIT: Thank you guys for your help. I was able to use your advice to solve the problem at hand. I would upvote you but apparently, I can't yet show my thanks in that form. I will return someday and give you your reputation bumps.

Can also be done using operator
Inside operator.itemgetter(1) you are indicating which index you want to sort on. So, in in this case we are specifying 1 which is the second item.
import operator
l = [[1,4,3], [2,2,4], [3,1,5]]
print(sorted(l, key=operator.itemgetter(1)))
output:
[[3, 1, 5], [2, 2, 4], [1, 4, 3]]

You can try like this,
>>> l = [[1,4,3], [2,2,4], [3,1,5]]
>>> sorted(l, key=lambda x: x[1])
[[3, 1, 5], [2, 2, 4], [1, 4, 3]]
or:
>>> l.sort(key=lambda x: x[1])
>>> l
[[3, 1, 5], [2, 2, 4], [1, 4, 3]]

Related

How to apply function to list inside list?

I am writing code to sum the second index in a list within a list:
employees = [[1, 5, [2, 3]], [2, 3, []], [3, 3, []]]
i.e. I want to do 5+3+3= 11
for i in range(len(employees)):
total_importance = sum(input[i][1])
or
total_important = sum(map(list.append, input[i][1]))
print(total_importance)
Now both of the options above for total_importance produce an error- int object is not itterable or other errors. Is there any way I can amend the code above for it to work?
I have another method where I simply append input[i][1] into a new list and then sum from there, and this easily works. But for my knowledge I want to ask if there is any way to amend either of the code above? Thanks
You can use list comprehension to extract the second element and sum on that new list:
sum([l[1] for l in employees])
The above code returns 11 on your employees object.
This also works without creating a new list, by passing a generator to the sum function:
sum(l[1] for l in employees)
You can take advantage of pythons powerful unpack features:
sum(second for first,second,*rest in employees)
This will unpack into three variables, first, second and the rest (in case of uneven lengths).
If you only care to use the second you can use:
sum(second for _,second,*_ in employees)
The line where you wrote total_importance = sum(input[i][1]), inside the sum() you are basically extracting one integer from that list and using sum() on it. This is equivalent to doing sum(2) which would of course cause an error.
Instead, you can do
total_importance = 0
for i in range(len(employees)):
num = employees[i][1]
total_importance += num
You might consider to reduce the list to the desired sum
reduce((lambda x, y: x+y[1]), employees, 0)
EDIT: Please note that input is a built in function in python. In the above mentioned reduce method x represents the "running sum" of summed up elements and y represents an item from the employees list. So x is an int value and y is a list. 0 is the initial value for the calculation:
>>> import functools
>>> employees = [[1, 5, [2, 3]], [2, 3, []], [3, 3, []]]
>>> functools.reduce((lambda x, y: x+y[1]), employees, 0)
11
So, this is because you are using sum function which works on list. But you are selecting individual element. So you need to do:
total_importance += employees[i][1]
You can make this code more general like even if the second element is list, it will work.
# employees = [[1, 5, [2, 3]], [2, 3, []], [3, 3, []]]
employees = [[1, [5,6], [2, 3]], [2, 3, []], [3, 3, []]]
total_importance=0
for i in range(len(employees)):
if isinstance(employees[i][1], list):
total_importance += sum(employees[i][1])
else:
total_importance += employees[i][1]
print(total_importance)

Unique list of lists

I have a nested list as an example:
lst_a = [[1,2,3,5], [1,2,3,7], [1,2,3,9], [1,2,6,8]]
I'm trying to check if the first 3 indices of a nested list element are the same as other.
I.e.
if [1,2,3] exists in other lists, remove all the other nested list elements that contain that. So that the nested list is unique.
I'm not sure the most pythonic way of doing this would be.
for i in range(0, len(lst_a)):
if lst[i][:3] == lst[i-1][:3]:
lst[i].pop()
Desired output:
lst_a = [[1,2,3,9], [1,2,6,8]]
If, as you said in comments, sublists that have the same first three elements are always next to each other (but the list is not necessarily sorted) you can use itertools.groupby to group those elements and then get the next from each of the groups.
>>> from itertools import groupby
>>> lst_a = [[1,2,3,5], [1,2,3,7], [1,2,3,9], [1,2,6,8]]
>>> [next(g) for k, g in groupby(lst_a, key=lambda x: x[:3])]
[[1, 2, 3, 5], [1, 2, 6, 8]]
Or use a list comprehension with enumerate and compare the current element with the last one:
>>> [x for i, x in enumerate(lst_a) if i == 0 or lst_a[i-1][:3] != x[:3]]
[[1, 2, 3, 5], [1, 2, 6, 8]]
This does not require any imports, but IMHO when using groupby it is much clearer what the code is supposed to do. Note, however, that unlike your method, both of those will create a new filtered list, instead of updating/deleting from the original list.
I think you are missing a loop For if you want to check all possibilities. I guess it should like :
for i in range(0, len(lst_a)):
for j in range(i, len(lst_a)):
if lst[i][:3] == lst[j][:3]:
lst[i].pop()
Deleting while going throught the list is maybe not the best idea you should delete unwanted elements at the end
Going with your approach, Find the below code:
lst=[lst_a[0]]
for li in lst_a[1:]:
if li[:3]!=lst[0][:3]:
lst.append(li)
print(lst)
Hope this helps!
You can use a dictionary to filter a list:
dct = {tuple(i[:3]): i for i in lst}
# {(1, 2, 3): [1, 2, 3, 9], (1, 2, 6): [1, 2, 6, 8]}
list(dct.values())
# [[1, 2, 3, 9], [1, 2, 6, 8]]

Remove duplicate list from list

First of all let me acknowledge that this question has been asked before, but the answers either seem outdated or unsatisfactory. The question is given a list of unsorted list, how can we remove duplicates in the most efficient and elegant manner? (i.e. using the shortest syntax vs. the fastest computational time)
Example:
Given [[1,2,3],[],[2,-2],[3,2,1]], we want [[1,2,3],[],[2,-2]]. Note that whether [1,2,3] or [3,2,1] doesn't matter.
You can do:
>>> li=[[1,2,3],[],[2,-2],[3,2,1]]
>>> {frozenset(e) for e in li}
{frozenset({1, 2, 3}), frozenset({2, -2}), frozenset()}
>>> [list(x) for x in {frozenset(e) for e in li}]
[[1, 2, 3], [2, -2], []]
The key is to use frozenset since a set is not hashable. Note the order may change with this method.
If you want to maintain the same order, you can do:
>>> seen=set()
>>> [e for e in li if frozenset(e) not in seen and not seen.add(frozenset(e))]
[[1, 2, 3], [], [2, -2]]
If there is a possibility of repeated elements within the sublists, you can sort the sublists and use a representation of that:
li=[[1,2,3],[],[2,-2],[3,2,1],[1,1,2,2,3],[1,2,1,2,3]]
seen=set()
nli=[]
for e in li:
re=repr(sorted(e))
if re not in seen:
seen.add(re)
nli.append(e)
>>> nli
[[1, 2, 3], [], [2, -2], [1, 1, 2, 2, 3]]
(Note: You can use tuple instead or repr if desired. Either produces a hashable immutable result)

Grouping nested list by changes in list lengths

I have a nested list in the following form
inputlist = [[1,2,3],[4,5,6],[7,8,9],[1,2,3,4],[5,6,7,8],[1,2],[3,4]]
I would like further nest it based on changing length as follows:
outputlist = [[[1,2,3],[4,5,6],[7,8,9]],[[1,2,3,4],[5,6,7,8]],[[1,2],[3,4]]]
The underlying logic is that I wish to group every change in list length into a new sublist. It is kind of difficult to explain but I hope the above two examples show what I am trying to do.
How can I achieve this simply and elegantly using python? Thanks.
>>> from itertools import groupby
>>> input_list = [[1,2,3],[4,5,6],[7,8,9],[1,2,3,4],[5,6,7,8],[1,2],[3,4]]
>>> [list(g) for k, g in groupby(input_list, key=len)]
[[[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[1, 2, 3, 4], [5, 6, 7, 8]], [[1, 2], [3, 4]]]
Here's an approach.
Get a list of the lengths involved:
#solen: set of lengths
solen = set([len(subl) for subl in inputlist]) # portable
solen = {len[subl] for subl in inputlist} # post Python 2.6
Then build the list of lists of a particular length:
#losubl: list of sublists, one for each item from solen
losubl = [[subl for subl in inputlist if len(subl) == ulen] for ulen in solen]
As jamylak points out, this solution is less efficient than the one based on itertools (more than one pass, sacrifices some order information). OTOH, it may avoid an import if you don't have other uses for itertools. If the lists you're working with are big and complicated, it's probably worth the extra import to use itertools.

Delete first item in each key in a dictionary

I'm kind of stumped here and would love some help.
So I have a dictionary like so
{'a':[1, 2, 3], 'b':[4, 5, 6]}
and I need to create a function which will automatically delete the first item in each list/key, so it should become
{'a':[2, 3], 'b':[5, 6]}
I know I should use the pop function, but it kind of gets confusing when there's way more than 2 keys and 3 values
You can use dictionary-comprehension for that:
>>> a = {'a':[1, 2, 3], 'b':[4, 5, 6]}
>>> {k: a[k][1:] for k in a}
{'a': [2, 3], 'b': [5, 6]}
Here's another solution,
dict1={'a':[1, 2, 3], 'b':[4, 5, 6]}
for item in dict1.values():
item.remove(item[0])
The for-loop moves through the values in the dictionary represented by 'dict1.values()'. The values are a set of lists, the '.remove()' function of the list removes the first occurrence of the element given, so if you give it the first element found in the list it would likewise remove it.
Hope this was useful!
You could achieve this with a one-liner using the map built-in function combined with a lambda function. Another advantage of this approach is that there's no copying involved (like in the slicing solution):
doc = {'a':[1, 2, 3], 'b':[4, 5, 6]}
map(lambda v: v.pop(0), doc.values())
This applies list.pop(0) on each value of your dict.
You can loop through the values of the dictionary and pop them one at a time. If you need further functionality or control, you can place it in a function.
dict_ = {'a':[1, 2, 3], 'b':[4, 5, 6]}
for val in dict_.values()
val.pop(0)
You can iterate through the dict & splice the list.
var1 = {'a':[1, 2, 3], 'b':[4, 5, 6]}
for element in var1:
var1[element] = var1[element][1:]

Categories

Resources