Rearrange list based on key WITHOUT sorting - python

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

Related

Python: Minimum of lists using custom function

If I create a list of tuples lst = [(0,5), (5,0), (2,2)] and use min(lst), it returns (0,5).
How does python decide which tuple to be the smallest?
Is it using first element of tuple to make a decision?
How should I change it to return minimum sum of tuple assuming there are only 2 elements in tuples?
You can use a custom key with min.
lst = [(0,5), (5,0), (2,2)]
res = min(lst, key=lambda x: sum(x))
# (2, 2)
To understand how min(lst) works in general, see How does the min/max function on a nested list work?
If you wish to retrieve element with minimum sum sorted by first element, you can specify a tuple key:
lst2 = [(0,5), (5,0), (2,2), (1,3)]
res = min(lst2, key=lambda x: (sum(x), x[0]))
# (1, 3)
Without using the tuple key, the result above would be (2,2).

Index a tuple by calling one of its elements

I'm trying to write a function that takes in
a list of tuples with partygoers names and their priority and
the max number of people that can get it. I'm currently trying to put tuples in ascending order based on one of its elements.
I have a list or tuples:
alist =[('Carlos', 1), ('Gretchen', 8), ('Abby', 6),('Sam', 4)]
so far Ive made a list of all of the numbers in order with this code:
def party_time(alist,maxnum):
newlist = []
finallist=[]
while len(finallist) < maxnum:
for tup in alist:
rank = tup[1]
newlist.append(rank)
newlist.sort()
newlist = [1,4,6,8]
Is there a way to use a for loop to go through newlist and index the tuple by using the number in newlist? Or will it just index where the number is in the tuple?
This should do it:
def party_time(alist, maxnum):
sorted_list = sorted(alist, key=lambda p: p[1], reverse=True)
return sorted_list[:maxnum]

Compare items in list if tuples 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).

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.

python - Common lists among lists in a list

I need to be able to find the first common list (which is a list of coordinates in this case) between a variable amount of lists.
i.e. this list
>>> [[[1,2],[3,4],[6,7]],[[3,4],[5,9],[8,3],[4,2]],[[3,4],[9,9]]]
should return
>>> [3,4]
If easier, I can work with a list of all common lists(coordinates) between the lists that contain the coordinates.
I can't use sets or dictionaries because lists are not hashable(i think?).
Correct, list objects are not hashable because they are mutable. tuple objects are hashable (provided that all their elements are hashable). Since your innermost lists are all just integers, that provides a wonderful opportunity to work around the non-hashableness of lists:
>>> lists = [[[1,2],[3,4],[6,7]],[[3,4],[5,9],[8,3],[4,2]],[[3,4],[9,9]]]
>>> sets = [set(tuple(x) for x in y) for y in lists]
>>> set.intersection(*sets)
set([(3, 4)])
Here I give you a set which contains tuples of the coordinates which are present in all the sublists. To get a list of list like you started with:
[list(x) for x in set.intersection(*sets)]
does the trick.
To address the concern by #wim, if you really want a reference to the first element in the intersection (where first is defined by being first in lists[0]), the easiest way is probably like this:
#... Stuff as before
intersection = set.intersection(*sets)
reference_to_first = next( (x for x in lists[0] if tuple(x) in intersection), None )
This will return None if the intersection is empty.
If you are looking for the first child list that is common amongst all parent lists, the following will work.
def first_common(lst):
first = lst[0]
rest = lst[1:]
for x in first:
if all(x in r for r in rest):
return x
Solution with recursive function. :)
This gets first duplicated element.
def get_duplicated_element(array):
global result, checked_elements
checked_elements = []
result = -1
def array_recursive_check(array):
global result, checked_elements
if result != -1: return
for i in array:
if type(i) == list:
if i in checked_elements:
result = i
return
checked_elements.append(i)
array_recursive_check(i)
array_recursive_check(array)
return result
get_duplicated_element([[[1,2],[3,4],[6,7]],[[3,4],[5,9],[8,3],[4,2]],[[3,4],[9,9]]])
[3, 4]
you can achieve this with a list comprehension:
>>> l = [[[1,2],[3,4],[6,7]],[[3,4],[5,9],[8,3],[4,2]],[[3,4],[9,9]]]
>>> lcombined = sum(l, [])
>>> [k[0] for k in [(i,lcombined.count(i)) for i in lcombined] if k[1] > 1][0]
[3, 4]

Categories

Resources