Compare items in list if tuples Python - python

If have this list of tuples:
l1 = [('aa', 1),('de', 1),('ac', 3),('ab', 2),('de', 2),('bc', 4)]
I want to loop over it, and check if the second item in each tuple is the same as another second item in a tuple in this list. If it is, I want to put this tuple in a new list of tuples.
So from this list of tuples I would expect for my new list of tuples:
l2 = [('aa', 1), ('de', 1), ('ab', 2),('de', 2)]
Because the one's and the two's match. Right now if have this:
l2 = []
for i in range(len(l1)):
if l1[i][1] in l1[:][0]:
l2.append(l1[i])
However, this only gives me:
l2 = [('aa', 1), ('de', 1)]
I'm pretty sure I'm not indexing the list of tuples right. The solution is probably pretty simple, but I'm just not getting there.

You'll need two passes: one to count how many times the second element exists, and another to then build the new list based on those counts:
from collections import Counter
counts = Counter(id_ for s, id_ in l1)
l2 = [(s, id_) for s, id_ in l1 if counts[id_] > 1]
Demo:
>>> from collections import Counter
>>> l1 = [('aa', 1),('de', 1),('ac', 3),('ab', 2),('de', 2),('bc', 4)]
>>> counts = Counter(id_ for s, id_ in l1)
>>> [(s, id_) for s, id_ in l1 if counts[id_] > 1]
[('aa', 1), ('de', 1), ('ab', 2), ('de', 2)]
Your code goes wrong with l1[:][0]; l1[:] just creates a shallow copy of the list, then takes the first element from that list. Even if it worked, your approach would have to check every other element in your list for every tuple you consider, which is really inefficient (the code would take N**2 steps for a list of N tuples).

Related

printing out tuples from function

Im making this function that prints the index number with the item from the string but I need it to print out as tuples, for example
[(0, 'dog'), (1, 'pig'), (2, 'cow')]
how would I go about doing this? it just prints each out on one line so far.
this is my code so far.
def my_enumerate(items):
"""return a list of tuples where item is the i'th item """
for item in items:
index = items.index(item)
print(item, index)
my_enumerate(['dog', 'pig', 'cow'])
thank you
def my_enumerate(items):
"""return a list of tuples where item is the i'th item """
l = []
for index in range(len(items)):
t = (index, items[index])
l.append(t)
return l
res = my_enumerate(['x', 'x', 'x'])
print(res)
I would suggest:
def my_enumerate(items):
print([(i, animal) for i, animal in enumerate(items)])
the reason you are printing them one by one is that you are printing them one by one inside the loop, to get all of them at once you will need to create a list and print that at the end of the loop. there are several ways to go about this and m0nte-cr1st0 gave you the most legible one, ill give you something a little shorter:
def my_enumerate(items):
return list(zip(range(len(items)), items))
zip lets you smoosh together 2 lists pairwise
this solution of course assumes you cant just use the built in enumerate
In expression
print(list(enumerate(items)))
part enumerate(['dog', 'pig', 'cow']) will create an iterator over the list of tuples [(0, 'dog'), (1, 'pig'), (2, 'cow')] you want to print , so you can just create a list with this iterator and print it.

Rearrange list based on key WITHOUT sorting

I have a list of tuples (x, ind) where x is the item and ind is it's target index in the resulting list. The list is in random order, but it can be assumed that if there are N items in the list, the values of ind in the tuples will be in [0,N) without repetition (i.e. all the valid indices will exist exactly once). How do I get a list where each tuple's position is ind?
Please do not confuse with the many existing answers of how to sort by key.
Obviously, sorting by the ind key is easy, but there would be the unnecessary extra O(n*logn) cost to what should be a O(n) operation because of the aforementioned assumption about ind values.
So:
l = [('item1',1), ('item0',0), ('item2',2), ('item4',4), ('item3',3)]
l2 = magic_rearrange(l, key=lambda x: x[1])
print(l2)
Should give:
[('item0',0), ('item1',1), ('item2',2), ('item3',3), ('item4',4)]
Assuming your indices are unique, here's one way. You can initialise a new list and just insert elements in their right position.
def magic_rearrange(l1):
l2 = [None] * len(l1)
for i in l1:
l2[i[1]] = i
return l2
And a demo:
>>> l = [('item1',1), ('item0',0), ('item2',2), ('item4',4), ('item3',3)]
>>> magic_rearrange(l)
[('item0', 0), ('item1', 1), ('item2', 2), ('item3', 3), ('item4', 4)]
There's a quicker way to do this, if you use numpy's fancy indexing.
import numpy as np
def magic_rearrange(l1):
l2 = np.repeat(None, len(l1))
l2[[x[1] for x in l1]] = l1
return l2
And a demo:
>>> magic_rearrange(l)
array([('item0', 0), ('item1', 1), ('item2', 2), ('item3', 3), ('item4', 4)], dtype=object)
Create the list first and then replace:
def magic_rearrange(l, key):
# creates list to not change original list
new_list = list(l)
# reorder on new list
for original_index, new_index in enumerate(map(key, l)):
new_list[new_index] = l[original_index]
return new_list
Here you go.
def magic_rearrange (input_list, key = lambda x: x):
result_list = [None] * len (input_list)
for p in input_list:
result_list[key (p)] = p
return result_list
We just create a list of the desired length, and then put each element in its place.
The order of operations can be arbitrary, but each element will eventually get to its position in the resulting list.
This is O(N) if copying a single list element and obtaining the key are both O(1).
The key = lambda x: x is for the default order, which is comparing the whole elements (however useless since the result is just list(range(N))).

How do I return the index of a group of values from a list for a specific value in the group?

If I have the list:
list1 = [(12, "AB", "CD"), (13, "EF", "GH"), (14, "IJ", "KL")]
I want to get the index of the group that has the value 13 in it:
if 13 in list1[0]:
idx = list1.index(13)
item = list1[idx]
print str(item)
[13, EF, GH]
When I try this, I keep getting "Index not in list", even though it is passing the if statement because it is finding the value 13 within the list.
You can use next and enumerate:
>>> list1 = [(12, "AB", "CD"), (13, "EF", "GH"), (14, "IJ", "KL")]
>>> next(i for i,x in enumerate(list1) if 13 in x)
1
With a simple for-loop:
for i, item in enumerate(list1):
if 13 in item:
print i
break
...
1
Update:
If the first item in each tuple is unique and you're doing this multiple times then create a dict first. Dicts provide O(1) lookup while lists O(N)
>>> list1 = [(12, "AB", "CD"), (13, "EF", "GH"), (14, "IJ", "KL")]
>>> dic = {x[0]:x[1:] for x in list1}
Accessing items:
>>> dic[12]
('AB', 'CD')
>>> dic[14]
('IJ', 'KL')
#checking key existence
>>> if 17 in dic: #if a key exists in dic then do something
#then do something
Given the added criterion from the comment "I really don't care where they are in the list" the task becomes much easier and far more obvious
def get_ids(id, tuple_list):
"""returns members from tuple_list whose first element is id"""
return [x for x in tuple_list if x[0] == id]
This isn't as expensive as one might expect if you recall that tuples are immutable objects. When the interpreter builds the new list, it only contains the internal id (reference) of the tuples of interest. This is in keeping with the original question asking for a list of indices. List comprehensions as used here are an efficient way of constructing new lists as much of the work is done internal to the interpreter. In short, many intuitions from C-like languages about performance don't apply well to Python.
As Ashwini noted, if the id numbers in the tuples are unique, and you are making multiple queries, then a dictionary might be a more suitable structure. Even if the id numbers aren't unique, you could use a dictionary of lists of tuples, but it is best to do the clearest thing first and not guess at the performance in advance.
As with the dictionary example, because an empty list is "falsey" in Python, you can use the same sort of conditional:
hits = get_ids(13, list1)
if hits:
# we got at least one tuple back
else:
# no 13s to be had

Searching List of Tuples by nth element in Python

So I have a list of tuples, with 3 elements each. Both the second and third elements are ints. For a value n, I need to return all the tuples that contain n as either the second or third element.
I'm not entirely sure how to do this, unfortunately. I'm sure it's not too complicated, but although there are some similar questions, I can't find any for this exact problem. Does anyone know how to go about this?
Thanks
You should be able to do this with a simple list comprehension. Something like:
[t for t in list_of_tuples if t[1] == n or t[2] == n]
Use a list comprehension with a simple if condition:
>>> lis=[('a',1,2),('b',2,2),('c',3,3),('d',3,1)]
>>> n=1
>>> [x for x in lis if n in x[1:3]] #[1:3] returns a sublist containing
# 2 & 3 element of each tuple
[('a', 1, 2), ('d', 3, 1)]
blist = [tup for tup in alist if n in tup[1:]]
The line above uses a list comprehension, and is equivalent to:
blist = []
for tup in alist:
if n in tup[1:]:
blist.append(tup)
tup[1:] returns a new tuple, consisting of the second and third items in the three item tuple tup.
In hindsight James Henstridge's example seems preferable, because t[1] == n or t[2] == n uses the existing tuple.

Is it possible to take an ordered "slice" of a dictionary in Python based on a list of keys?

Suppose I have the following dictionary and list:
my_dictionary = {1:"hello", 2:"goodbye", 3:"World", "sand":"box"}
my_list = [1,2,3]
Is there a direct (Pythonic) way to get the key-value pairs out of the dictionary for which the keys are elements in the list, in an order defined by the list order?
The naive approach is simply to iterate over the list and pull out the values in the map one by one, but I wonder if python has the equivalent of list slicing for dictionaries.
Don't know if pythonic enough but this is working:
res = [(x, my_dictionary[x]) for x in my_list]
This is a list comprehension, but, if you need to iterate that list only once, you can also turn it into a generator expression, e.g. :
for el in ((x, my_dictionary[x]) for x in my_list):
print el
Of course the previous methods work only if all elements in the list are present in the dictionary; to account for the key-not-present case you can do this:
res = [(x, my_dictionary[x]) for x in my_list if x in my_dictionary]
>>> zip(my_list, operator.itemgetter(*my_list)(my_dictionary))
[(1, 'hello'), (2, 'goodbye'), (3, 'World')]
How about this? Take every item in my_list and pass it to the dictionary's get method. It also handles exceptions around missing keys by replacing them with None.
map(my_dictionary.get, my_list)
If you want tupples zip it -
zip(my_list, map(my_dictionary.get, my_list))
If you want a new dict, pass the tupple to dict.
dict(zip(my_list, map(my_dictionary.get, my_list)))
A straight forward way would be to pick each item from the dictionary and check if the key is present in the list
>>> [e for e in my_dictionary.items() if e[0] in my_list]
[(1, 'hello'), (2, 'goodbye'), (3, 'World')]
The above search would be linear so you might gain some performance by converting the list to set
>>> [e for e in my_dictionary.items() if e[0] in set(my_list)]
[(1, 'hello'), (2, 'goodbye'), (3, 'World')]
And finally if you need a dictionary instead of a list of key,value pair tuples you can use dictionary comprehension
>>> dict(e for e in my_dictionary.items() if e[0] in set(my_list))
{1: 'hello', 2: 'goodbye', 3: 'World'}
>>>

Categories

Resources