Find matching items in nested list - python

I'm trying to match a list of items to another list of items of a different dimension and print an element from the second list if the item is matched. For example:
stlist=['6', '3', '4', '2', '5', '1']
ndlist=[['Tom', '1'], ['Joh', '2'], ['Sam', '3'], ['Tommy','4'], ['Nanni', '5'], ['Ron', '6']]
My outputlist is producing the names in the ascending order of my stlist.
i.e Tom, Joh, Sam, Tommy, Nanni, Ron but I want the outputlist to be in the same order as the stlist.
My Python code is:
for sublist in ndlist:
for element in stlist:
if element in sublist[1]:
print(sublist[0])
The outputlist displayed from the above codes is: Tom, Joh, Sam, Tommy, Nanni, Ron instead of
outputlist = [Ron, Sam, Tommy, Joh, Nanni, Tom]
So it's actually sorting in ascending order my 1stlist and printing the names from the 2ndlist in that order.But if my stlist was in ascending order the outputlist would be fine.
Can anyone tell me why please and how should I modify the codes to get my desired outputlist.

Try to rearrange your for loops:
for element in stlist:
for sublist in ndlist:
if element in sublist[1]:
print (sublist[0])
Also, the if statement should maybe be like this: if element == sublist[1]: or else the element '1' would be found in some ndlist element like this one: ['foo', '10']
Furthermore, this solution is not the most efficient with large lists. To make it more efficient you could try something like sorting the ndlist and performing binary search to check if an element exists.

You could use sorted and a custom sort key (a Lambda function) to do this:
>>> [i[0] for i in sorted(ndlist, key = lambda x:stlist.index(x[1]))]
['Ron', 'Sam', 'Tommy', 'Joh', 'Nanni', 'Tom']
This line of code sorts ndlist using the position of the numbers in stlist as the sort key, then a list comprehension builds a new list containing only the names.

Instead of nesting loops or sorting you could also create a mapping with one linear run through ndlist and then use another linear run through stlist to build the result list:
mapping = dict((b, a) for a, b in ndlist)
result = [mapping[x] for x in stlist]
print(result)

Related

Repeat items in list and maintain order of occurrence until maximum length of list is reached

I have a list of values in a list. The length of the list is variable from 1-5. If the list is less than the maximum_length then the list should be expanded, but should never exceed the max length.
I want to expand the list incrementally to distribute the items across the list starting from the first item. Below is the input list and the desired output. The amount of items duplicated is correct, but the order is not.
This list can be string values, so simply sorting the list is not a solution.
Example 1
current_list: ['#f8f8f8']
result: ['#f8f8f8', '#f8f8f8', '#f8f8f8', '#f8f8f8', '#f8f8f8']
desired result: ['#f8f8f8', '#f8f8f8', '#f8f8f8', '#f8f8f8', '#f8f8f8']
Example 2
current_list: ['#090909', '#171717']
result: ['#090909', '#171717', '#090909', '#171717', '#090909']
desired result: ['#090909', '#090909', '#090909', '#171717', '#171717']
Example 3
current_list: ['#d8d8d8', '#ececec', '#f1f1f1']
result: ['#d8d8d8', '#ececec', '#f1f1f1', '#d8d8d8', '#ececec']
desired result: ['#d8d8d8', '#d8d8d8', '#ececec', '#ececec', '#f1f1f1']
Example 4
current_list: ['#ecede7', '#eff0eb', '#f1f2ed', '#ffffff']
result: ['#ecede7', '#eff0eb', '#f1f2ed', '#ffffff', '#ecede7']
desired result: ['#ecede7', '#ecede7', '#eff0eb', '#f1f2ed', '#ffffff']
Below is my code to produce the list. How can I update this to get the desired output?
print([current_list * max_len][0][:max_len])
Updated to show that using sort isn't what I need. The purpose of this question is to maintain the distribution of the original list.
Use sort as explained in the comments and to maintain the distribution of the original list, the sort key should be the index of the element in your original list.
max_len = 5
current_list = ['1','2','3','4']
inter = [current_list * max_len][0][:max_len] # assume we stick with your function
# ['1', '2', '3', '4', '1']
sorted(inter, key=current_list.index)
# ['1', '1', '2', '3', '4']
Works with any order, sorts on the original order.
current_list = ['2','3','4', '1']
inter = [current_list * max_len][0][:max_len]
sorted(inter, key=current_list.index)
# ['2', '2', '3', '4', '1']
If your list is larger, you can use a more efficient key for the sort, like a dictionary which stores the elements and keys and their index as values.
Here are some other answers discussing sorting like this.

Get a slice of a sorted list according to a key in Python

Is it possible to slice a sorted list based on some key value (e.g the length of the list items)? If so, how?
For example I get a sorted list like this:
sorted_list = sorted(some_list, key=len)
Now I want to get a slice containing all items with the lowest and equal len (i.e. all items of len: min(sorted_list, key=len) ) which should be the at the head of the sorted list.
You can group the items first, then take the elements of the first resulting subiterator.
from itertools import groupby
firsts = list(next(groupby(sorted(some_list, key=len), len))[1])
For example,
>>> some_list = [[1, 2, 3], [4, 5, 6], [1], [2], [2, 3]]
>>> list(next(groupby(sorted(some_list, key=len), len))[1])
[[1], [2]]
You can do it like this:
min_len = len(min(some_list, key=len))
sorted_list = sorted((x for x in some_list if len(x) == min_len), key=len)
What this does is it finds the length of the minimum element in the list, and then filters out the elements that are longer than that when the list is passed into the sorted function. It requires an extra pass over the data to find the min length, but sorting takes much longer than that, so that time-cost is practically irrelevant.
This function sorts the list, gets the key (i.e. the shortest length element), then builds an array containing only elements of equal length.
def slice_list(lst, func=len):
# start by sorting the list
sorted_lst = sorted(lst, key=func)
# get the key
key = func(sorted_lst[0])
# get the slice
slice = [v for v in sorted_lst if func(v) <= key]
return slice
Since there are no test cases, here is one (if I am interpreting this question correctly)
test = ['abcd', 'abcde', 'efgh', '1234', 'abcdef']
print(slice_list(test, len))
Outputs
['abcd', 'efgh', '1234']
This is more for curiosity than practicality (since itertools is really performant)…you can group by a minimum and avoid the sort in a single pass in the same way you can find the minimum of a list without sorting. Here you just keep track of the current minimum and all the values of the same size. If you find a smaller value scrap the old ones and start again:
some_list = ['999', '11', '22', '343', '12', '545', '99', '11', '100', '11']
def minGroup(l, f):
it = iter(l)
current = [next(it)]
curr_min = f(current[0])
for item in it:
if f(item) < curr_min:
curr_min, current = f(item), [item]
elif f(item) == curr_min:
current.append(item)
return current
minGroup(some_list, len)
# ['11', '22', '12', '99', '11', '11']
minGroup(some_list, int)
# ['11', '11', '11']

How to convert a list with strings into a 2D list

I have a list like this:
list = ['aa#1#ll', 'bb#2#ff', 'cc#3#ff']
I want to convert it to a 2D list in this format
list2 = [['aa', '1', 'll'], ['bb', '2', 'ff'], ['cc', '3', 'ff']]
How can I do this in Python 3?
You can use Python's split(delimiter) method inside a generator:
list2 = [x.split("#") for x in list]
Explanation: Here, for each string x in the original list, we are calling x.split("#") on it, which will give a list of substrings that are separated by hashes in x, and we are adding the result to the list list2.

How to manipulate a tuple that contains multiple tuple

For example, I have this tuple:
data =(('name/score','game1', 'game2', 'game3', 'game4', 'game5'),('A','1','2','3','4','5'),('B','6','7','8','9','10'),('C','11','12','13','14','15'))
so data is a tuple that contains 4 smaller tuples that contains strings.
Actually data is a 'table' which shows the name A, B, C and D and their respective scores.
How to manipulate data, so that I extra informations I want in data?
For example,
1. how to split data into smaller tuples such as
tuple1 = ('name/score','game1', 'game2', 'game3', 'game4', 'game5')
tuple2 = ('A','1','2','3','4','5')
and so on?
How to remove the 'names', which are A, B and C in each smaller tuple?
I did it by slicing:
newtuple = tuple1[1:]
Just wondering if there is a recursive way or iterative way to do it, cause I dont really get the idea of iteration and recursion.
Is there anyway to define a function which can retrieve the data I want?
for example, I want to know to score of A in game 3, the function should return "3".
data =(('name/score','game1', 'game2', 'game3', 'game4', 'game5'),('A','1','2','3','4','5'),('B','6','7','8','9','10'),('C','11','12','13','14','15'))
The first element of your tuple is a sort of header (like in an excel file the first line).
You want to construct a dictionary of dictionaries where the first level keys are the users (A, B, C, etc.) and the second level of dictionaries have keys like game1, game2, etc. with the value representing the score reached in the given game.
D = dict((t[0], dict(zip(data[0][1:], t[1:]))) for t in data[1:])
dict(zip(data[0][1:], t[1:])) is the part where you create a dictionary from every tuple of data (starting the second tuple) as values using keys from the first tuple of data ("game1", "game2", etc.). We deliberately ignore the very first element of all tuples: "name/score" is ignored, and the user names "A", "B", etc. are also ignored.
Then we "attach" to each dictionary obtained above to a key which is the username: (t[0], dict(zip.... and we obtain a tuple.
Finally from the list of tuples we create a dictionary using the dict builtin function.
The above code will convert your input tuple of tuples to a dictionary of dictionaries like:
{'A': {'game1': '1', 'game2': '2', 'game3': '3', 'game4': '4', 'game5': '5'},
'B': {'game1': '6', 'game2': '7', 'game3': '8', 'game4': '9', 'game5': '10'},
'C': {'game1': '11', 'game2': '12', 'game3': '13', 'game4': '14', 'game5': '15'}}
To get the score of user A in game3 you write:
>>D["A"]["game3"]
3
Since you commented that you don't want to use dictionaries, here is a function that should satisfy your needs:
def get_score(D, user, game):
i = D[0].index(game)
for t in D[1:]:
if t[0] == user:
return t[i]
print get_score(data, "A", "game3")

I have a list of list in python and I want to strip some characters from every two elements

Probably this simle but somehow I am not able to resolve this. I have a list of list from which I want to strip some characters from every two elements and make another list of list
list = [['A*68:02:01:03', 'A*33:01:01', '1'],
['A*68:02:01:02', 'A*33:01:01', '1'],
['A*68:02:01:01', 'A*33:01:01', '1']]
required output = [['A*68:02', 'A*33:01', '1'],
['A*68:02', 'A*33:01', '1'],
['A*68:02', 'A*33:01', '1']]
Finally I want to print only unique elements. Like in the above case all three elements are same so the output should be:
output = ['A*68:02', 'A*33:01', '1']
Thanks for the help
>>> lst = [['A*68:02:01:03', 'A*33:01:01', '1'], ['A*68:02:01:02', 'A*33:01:01', '1'], ['A*68:02:01:01', 'A*33:01:01', '1']]
>>> newLst = [tuple(':'.join(data.split(':', 2)[:2]) for data in sublist) for sublist in lst]
>>> set(newLst)
{('A*68:02', 'A*33:01', '1')}
The interesting bit is the ':'.join(data.split(':', 2)[:2]. The will split data by colons, take only the first two parts and join them again. That way, we strip everything off after the second colon (including that).
The rest is just a list comprehension to go through the nested list. We also need to convert the inner list to a tuple, so they are hashable when we call set() on it. Doing that will get rid of all duplicates.
I think you want:
lst = [['A*68:02:01:03', 'A*33:01:01', '1'],
['A*68:02:01:02', 'A*33:01:01', '1'],
['A*68:02:01:01', 'A*33:01:01', '1']]
output = []
for item in lst:
processed = [":".join(s.split(":")[:2]) for s in item]
if processed not in output:
output.append(processed)
Note: don't call your own variables things like list.
You could do something like this:
def prefix(string):
"""
Returns the prefix of a string, including all characters
up until the second colon.
"""
return ":".join(string.split(":", 2)[:2])
def unique(iterable):
"""
Returns the unique elements in iterable, maintaining the
elements' relative order.
"""
result = []
seen = set()
for el in iterable:
if el not in seen:
seen.add(el)
result.append(el)
return result
L = [
['A*68:02:01:03', 'A*33:01:01', '1'],
['A*68:02:01:02', 'A*33:01:01', '1'],
['A*68:02:01:01', 'A*33:01:01', '1'],
]
prefixes = [(prefix(el[0]), prefix(el[1]), el[2]) for el in L]
# The built-in class set accepts an iterable and returns a set,
# an object with all duplicate elements removed. Since sets are
# unordered, converting the set back to a list will likely
# produce a list in which the original elements have lost their
# relative order.
# If this is a problem you can use the unique function from above.
uniq = list(set(prefixes))
# If you really need a list of lists, instead of a list of tuples.
uniq = [list(el) for el in uniq]
I have renamed your input list to L, because naming it list shadows the built-in function list.

Categories