Converting this loop to a list comprehension - python

I would like to generate a list of unique Ids by only keeping the list that has the minimum value in element 2.
For example, given the list:
list1 = [['Id1', 1, 40],['Id1', 2, 30],['Id2', 10,40]]`
Expected output:
[['Id1', 1, 40],['Id2', 10,40]]
Here's my working example, but it's pretty clunky. I think it could probably be done with a single list comprehension.
list1 = [['Id1', 1, 40],['Id1', 2, 30],['Id2', 10,40]]
unique_list = list(set([x[0] for x in list1]))
unique_list = [[x] for x in unique_list]
for x in unique_list:
id = x[0]
min_val = min([y[1:] for y in list1 if y[0] == id])
x.extend(min_val )
print unique_list

You can use itertools.groupby to group by the first element in the sublists, the you can get the min with a key argument to sort by the remaining elements in the sublist.
>>> from itertools import groupby
[min(list(g), key = lambda i: i[1:]) for k, g in groupby(list1, lambda i: i[0])]
[['Id1', 1, 40], ['Id2', 10, 40]]

A very naive approach but easily understandable.
list1 = [['Id1', 1, 40],['Id1', 2, 30],['Id2', 10,40]]
unique_list = []
for list_element in list1:
appendable = True
for temp_list in unique_list:
if list_element[0] == temp_list[0]:
if temp_list[1] < list_element[1]:
appendable = False
else:
unique_list.remove(temp_list)
if appendable == True:
unique_list.append(list_element)
unique_list.sort()
print unique_list

Related

How to replace a numerical value in a Python list

So I got this list of lists:
lst = [[0,1],2,[3]]
and I got a list of tuples:
lst_2 = [("x1","y1"),("x2","y2"), ("x3","y3"), ("x4","y4")]
I want to replace values inside lst with the index 0 value of each of the tuples in lst_2, and the tuple taken depends on the numerical value in lst. So it becomes:
lst = [["x1","x2"], "x3", ["x4"]]
Please don't roast me thanks so much
Try this:
lst = [[0,1],2,[3]]
lst_2 = [("x1","y1"),("x2","y2"), ("x3","y3"), ("x4","y4")]
res = []
for l in lst:
if isinstance(l, list):
res += [[lst_2[i][0] for i in l]]
else:
res += [lst_2[l][0]]
print(res)
Or with List Comprehensions:
res = [[lst_2[i][0] for i in l] if isinstance(l, list) else lst_2[l][0] for l in lst]
[['x1', 'x2'], 'x3', ['x4']]
You could use recursion to allow lst to have deeper levels of nesting:
def produce(template, data):
return [
produce(nested, data) for nested in template
] if isinstance(template, list) else data[template][0]
# Example
lst = [[0,[1]],2,[3]]
lst_2 = [("x1","y1"),("x2","y2"), ("x3","y3"), ("x4","y4")]
result = produce(lst, lst_2)

How to split a list based on tuple content in Python

I have a list of 3-tuples, and I want to split them on the last coordinate. So for example if the input were:
[(1,2,x), (2,3,x), (1,2,z), (2,20,z)]
I'd want the output to be
[(1,2,x),(2,3,x)],[(1,2,z),(2,20,z)]
My current approach involves getting all N distinct 3rd indices and looping over the list N times to build the list of lists. I am wondering if there is a pythonic way to do this without looping multiple times.
here is one way with complexity of O(n):
lt = [(1,2,'x'), (2,3,'x'), (1,2,'z'), (2,20,'z')]
res = {}
for i in lt:
res.setdefault(i[2], []).append(i)
print(list(res.values()))
output:
>>>
[[(1, 2, 'x'), (2, 3, 'x')], [(1, 2, 'z'), (2, 20, 'z')]]
nested list comprehension
if you dont know third_indicesa priori:
my_list = [(1,2,'x'), (2,3,'x'), (1,2,'z'), (2,20,'z')]
third_indices = set([ix[-1] for ix in my_list])
[[item for item in my_list if item[-1]==ix] for ix in third_indices]
Similar to #eshirvana, but using defaultdict, which is very performant and common for this type of problem. I would make a defaultdict and save lists of tuples based the key determined by the last element. Then all your groups will be in the values()
from collections import defaultdict
l = [(1,2,'x'), (2,3,'x'), (1,2,'z'), (2,20,'z'), (3,4,'x')]
groups = defaultdict(list)
for t in l:
groups[t[-1]].append(t)
list(groups.values())
# [[(1, 2, 'x'), (2, 3, 'x'), (3, 4, 'x')], [(1, 2, 'z'), (2, 20, 'z')]]
You can do something like this:
my_list = [(1,2,x), (2,3,x), (1,2,z), (2,20,z)]
list_x = [item for item in my_list if item[2] == x]
list_y = [item for item in my_list if item[2] == y]
list_z = [item for item in my_list if item[2] == z]
Edit: If x,y and z are not numbers then this should work:
my_list = [(1,2,'x'), (2,3,'x'), (1,2,'z'), (2,20,'z')]
list_x = [item for item in my_list if item[2] == 'x']
list_y = [item for item in my_list if item[2] == 'y']
list_z = [item for item in my_list if item[2] == 'z']
If you want them in the same list then do this
final_list = list((list_x,list_z))

adding empty string while joining the 2 lists - Python

I have 2 lists
mainlist=[['RD-12',12,'a'],['RD-13',45,'c'],['RD-15',50,'e']] and
sublist=[['RD-12',67],['RD-15',65]]
if i join both the list based on 1st element condition by using below code
def combinelist(mainlist,sublist):
dict1 = { e[0]:e[1:] for e in mainlist }
for e in sublist:
try:
dict1[e[0]].extend(e[1:])
except:
pass
result = [ [k] + v for k, v in dict1.items() ]
return result
Its results in like below
[['RD-12',12,'a',67],['RD-13',45,'c',],['RD-15',50,'e',65]]
as their is no element in for 'RD-13' in sublist, i want to empty string on that.
The final output should be
[['RD-12',12,'a',67],['RD-13',45,'c'," "],['RD-15',50,'e',65]]
Please help me.
Your problem can be solved using a while loop to adjust the length of your sublists until it matches the length of the longest sublist by appending the wanted string.
for list in result:
while len(list) < max(len(l) for l in result):
list.append(" ")
You could just go through the result list and check where the total number of your elements is 2 instead of 3.
for list in lists:
if len(list) == 2:
list.append(" ")
UPDATE:
If there are more items in the sublist, just subtract the lists containing the 'keys' of your lists, and then add the desired string.
def combinelist(mainlist,sublist):
dict1 = { e[0]:e[1:] for e in mainlist }
list2 = [e[0] for e in sublist]
for e in sublist:
try:
dict1[e[0]].extend(e[1:])
except:
pass
for e in dict1.keys() - list2:
dict1[e].append(" ")
result = [[k] + v for k, v in dict1.items()]
return result
You can try something like this:
mainlist=[['RD-12',12],['RD-13',45],['RD-15',50]]
sublist=[['RD-12',67],['RD-15',65]]
empty_val = ''
# Lists to dictionaries
maindict = dict(mainlist)
subdict = dict(sublist)
result = []
# go through all keys
for k in list(set(list(maindict.keys()) + list(subdict.keys()))):
# pick the value from each key or a default alternative
result.append([k, maindict.pop(k, empty_val), subdict.pop(k, empty_val)])
# sort by the key
result = sorted(result, key=lambda x: x[0])
You can set up your empty value to whatever you need.
UPDATE
Following the new conditions, it would look like this:
mainlist=[['RD-12',12,'a'], ['RD-13',45,'c'], ['RD-15',50,'e']]
sublist=[['RD-12',67], ['RD-15',65]]
maindict = {a:[b, c] for a, b, c in mainlist}
subdict = dict(sublist)
result = []
for k in list(set(list(maindict.keys()) + list(subdict.keys()))):
result.append([k, ])
result[-1].extend(maindict.pop(k, ' '))
result[-1].append(subdict.pop(k, ' '))
sorted(result, key=lambda x: x[0])
Another option is to convert the sublist to a dict, so items are easily and rapidly accessible.
sublist_dict = dict(sublist)
So you can do (it modifies the mainlist):
for i, e in enumerate(mainlist):
data: mainlist[i].append(sublist_dict.get(e[0], ""))
#=> [['RD-12', 12, 'a', 67], ['RD-13', 45, 'c', ''], ['RD-15', 50, 'e', 65]]
Or a one liner list comprehension (it produces a new list):
[ e + [sublist_dict.get(e[0], "")] for e in mainlist ]
If you want to skip the missing element:
for i, e in enumerate(mainlist):
data = sublist_dict.get(e[0])
if data: mainlist[i].append(data)
print(mainlist)
#=> [['RD-12', 12, 'a', 67], ['RD-13', 45, 'c'], ['RD-15', 50, 'e', 65]]

Sum lists with different lengths in python

I have 3 lists with different lengths. They are made like this:
final_list = [[1230, 0], [1231,0],[1232,0], [1233, 0], [1234, 0]]
list2 = [[1232, 20], [1233, 30]]
list3 = [[1230, 10], [1231,20],[1232,40]]
What I want to obtain the final_list like this:
final_list = [[1230, 10], [1231,20],[1232,60], [1233, 30], [1234, 0]]
(If, considering each element of list2 and list3, its first value is equal to one of the first elements of the final list, then the corresponding element of the final list has to have the second value equal to the sum of the elements found.)
Not a clean solution, but easy to grasp and might save your day.
f = {}
dcts = map(lambda l: dict([l]), list2+list3)
for dct in dcts:
for k in dct.iterkeys():
f[k] = w.get(k, 0) + d[k]
final_list = map(list, f.items())
however, if you are familiar with itertools
import groupby from itertools
merged = list2+list3
final_list = []
for key, group in groupby(merged, key = lambda e: e[0]):
final_list.append([key, sum(j for i, j in group)])
or a oneliner
[[k, sum(j for i, j in g)] for k, g in groupby(list3+list2, key = lambda e: e[0])]
I created a temp_list and append all three lists to it.
create a dictionary dic and loop through temp_list to sum up each tuple base on the key.
then I turn the dic back into a list and sort it.
I admit this is not the most efficient way to do this. but it is a solution.
temp_list = []
temp_list.append(final_list)
temp_list.append(list2)
temp_list.append(list3)
dic = {}
for lst in temp_list:
for tp in lst:
if tp[0] in dic:
dic[tp[0]] = dic[tp[0]] + tp[1]
else:
dic[tp[0]] = tp[1]
result = []
for key, value in dic.iteritems():
temp = [key,value]
result.append(temp)
result.sort()
result:
[(1230, 10), (1231, 20), (1232, 60), (1233, 30), (1234, 0)]

Get a unique list of items that occur more than once in a list

I have a list of items:
mylist = ['A','A','B','C','D','E','D']
I want to return a unique list of items that appear more than once in mylist, so that my desired output would be:
[A,D]
Not sure how to even being this, but my though process is to first append a count of each item, then remove anything equal to 1. Then dedupe, but this seems like a really roundabout, inefficient way to do it, so I am looking for advice.
You can use collections.Counter to do what you have described easily:
from collections import Counter
mylist = ['A','A','B','C','D','E','D']
cnt = Counter(mylist)
print [k for k, v in cnt.iteritems() if v > 1]
# ['A', 'D']
>>> mylist = ['A','A','B','C','D','E','D']
>>> set([i for i in mylist if mylist.count(i)>1])
set(['A', 'D'])
import collections
cc = collections.Counter(mylist) # Counter({'A': 2, 'D': 2, 'C': 1, 'B': 1, 'E': 1})
cc.subtract(cc.keys()) # Counter({'A': 1, 'D': 1, 'C': 0, 'B': 0, 'E': 0})
cc += collections.Counter() # remove zeros (trick from the docs)
print cc.keys() # ['A', 'D']
Try some thing like this:
a = ['A','A','B','C','D','E','D']
import collections
print [x for x, y in collections.Counter(a).items() if y > 1]
['A', 'D']
Reference: How to find duplicate elements in array using for loop in Python?
OR
def list_has_duplicate_items( mylist ):
return len(mylist) > len(set(mylist))
def get_duplicate_items( mylist ):
return [item for item in set(mylist) if mylist.count(item) > 1]
mylist = [ 'oranges' , 'apples' , 'oranges' , 'grapes' ]
print 'List: ' , mylist
print 'Does list have duplicate item(s)? ' , list_has_duplicate_items( mylist )
print 'Redundant item(s) in list: ' , get_duplicate_items( mylist )
Reference https://www.daniweb.com/software-development/python/threads/286996/get-redundant-items-in-list
Using a similar approach to others here, heres my attempt:
from collections import Counter
def return_more_then_one(myList):
counts = Counter(my_list)
out_list = [i for i in counts if counts[i]>1]
return out_list
It can be as simple as ...
print(list(set([i for i in mylist if mylist.count(i) > 1])))
Use set to help you do that, like this maybe :
X = ['A','A','B','C','D','E','D']
Y = set(X)
Z = []
for val in Y :
occurrences = X.count(val)
if(occurrences > 1) :
#print(val,'occurs',occurrences,'times')
Z.append(val)
print(Z)
The list Z will save the list item which occur more than once. And the part I gave comment (#), that will show the number of occurrences of each list item which occur more than once
Might not be as fast as internal implementations, but takes (almost) linear time (since set lookup is logarithmic)
mylist = ['A','A','B','C','D','E','D']
myset = set()
dups = set()
for x in mylist:
if x in myset:
dups.add(x)
else:
myset.add(x)
dups = list(dups)
print dups
another solution what's written:
def delete_rep(list_):
new_list = []
for i in list_:
if i not in list_[i:]:
new_list.append(i)
return new_list
This is my approach without using packages
result = []
for e in listy:
if listy.count(e) > 1:
result.append(e)
else:
pass
print(list(set(result)))

Categories