Python 3: convert a list into a dictionary - python

I am looking for a simple method to convert a list into a dictionary. I have a simple list:
leet =['a','4','b','l3','c','(','d','[)','e','3','g','6','l','1','o','0','s','5','t','7','w','\/\/']
which I want to easily convert to a dictionary. I have tried using defaultdict but I don't quite understand what it is doing ( I found this code in a previous answer):
>>> from collections import defaultdict
>>> dic = defaultdict(list)
>>> for item in leet:
key ="/".join(item[:-1])
dic[key].append(item[-1])
>>> dic
defaultdict(<class 'list'>, {'\\:/:\\': [], '': ['a', '4', 'b', 'c', '(', 'd', 'e', '3', 'g', '6', 'l', '1', 'o', '0', 's', '5', 't', '7', 'w'], 'l': ['3'], '[': [')'], '\\///\\': ['/']})
Ultimately, I want to read in the data from a txt file ( line by line) into a list and convert to a dictionary for the rest of the simple program.
I'm looking for a straight-forward way to achieve this.
Thanks

Not sure you're going down the right path with a defaultdict, convert to a dict by grouping into pairs, then use dict.get to cater for when there isn't a matching key:
leet =['a','4','b','l3','c','(','d','[)','e','3','g','6','l','1','o','0','s','5','t','7','w','\/\/']
lookup = dict(zip(*[iter(leet)] * 2))
text = 'how are you?'
blah = ''.join(lookup.get(ch, ch) for ch in text)
# h0\/\/ 4r3 y0u?

components_dict = dict(((lambda y: y['id'])(y), y) for y in components)
component object as follows:
{"id":1234, "name":"xxx"}

Related

Dict of list of list into dict of tuples of tuples

I have this dictionary:
a = {'Jimmy': [['5', '7', '5'], ['S', 'F', 'R']],
'Limerick': [['8', '8', '5', '5', '8'], ['A', 'A', 'B', 'B', 'A']]}
I want the end product to be a dictionary with tuples of tuples, but I think the second loop is not working.
What I want:
a = {'Jimmy': (('5', '7', '5'), ('S', 'F', 'R')),
'Limerick': (('8', '8', '5', '5', '8'), ('A', 'A', 'B', 'B', 'A'))}
Can anyone help me to see what I'm doing wrong?
I tried:
a = {'Jimmy': [['5', '7', '5'], ['S', 'F', 'R']],
'Limerick': [['8', '8', '5', '5', '8'], ['A', 'A', 'B', 'B', 'A']]}
for key in a:
a[key] = tuple(a[key])
for value in a[key]:
value = tuple(value)
print(a)
but it didn't work.
value refers to a fresh variable -- reassigning it does not modify the dictionary.
You should use map() to transform each list into a tuple, and then call tuple() once more to transform the resulting map object into a tuple:
for key in a:
a[key] = tuple(map(tuple, a[key]))
You are almost there. What you needed is this:
for key in a:
a[key] = tuple(tuple(item) for item in a[key])
Your statement value = tuple(value) re-assigns the local variable value to a new tuple, but it doesn't change the contents of a[key] at all.
In fact, since tuples are immutable, your statement a[key] = tuple(a[key]) prevents the contents of a[key] from changing, unless you reassign a[key] = something_else. Something like a[key] = tuple(a[key]) then a[key][0] = "A" will fail because tuples are immutable.
The other answers give nice concise solutions, so you may want to go with those, but here is one that mirrors your original attempt:
a = {'Jimmy': [['5', '7', '5'], ['S', 'F', 'R']],
'Limerick': [['8', '8', '5', '5', '8'], ['A', 'A', 'B', 'B', 'A']]}
for key in a:
new_values = []
for value in a[key]:
new_values.append(tuple(value))
a[key] = tuple(new_values)
print(a)
Here, to get around the fact that tuples are immutable, you can make a list [] (which is mutable), build it up over the loop, then convert the list to a tuple, and then finally assign that tuple to a[key].

How do I sort a list by another list's index number in Python?

When we sort a dictionary by value in Python, we use lambda func. But i tried this way in my lists, instead of values, using another list index number. This may be a short str function/method for my question.
Here is an example:
cardsorder = ['2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A']
colors = ['C', 'D', 'S', 'H']
handEx = ['8C','TS','KC','9H','4S']
I want to sort my handEx list elements by using cardsorder index numbers.
Firstly i scan every element and i find values for sorting:
for card in handEx:
value = cardsorder.index(card[0])
In this way by using these values, i can sort elements by comparing the others but i think it is a very long way to find the solution.
Is there a simpler solution, for example using a lambda function?
You're very close. Just make a key function that gets the equivalent of value:
>>> sorted(handEx, key=lambda card: cardsorder.index(card[0]))
['4S', '8C', '9H', 'TS', 'KC']
Or in-place:
handEx.sort(key=lambda card: cardsorder.index(card[0]))
By the way, I posted a similar answer here.

How to write complex sort in python?

Is there a concise way to sort a list by first sorting numbers in ascending order and then sort the characters in descending order?
How would you sort the following:
['2', '4', '1', '6', '7', '4', '2', 'K', 'A', 'Z', 'B', 'W']
To:
['1', '2', '2', '4', '4', '6', '7', 'Z', 'W', 'K', 'B', 'A']
One way (there might be better ones) is to separate digits and letters beforehand, sort them appropriately and glue them again together in the end:
lst = ['2', '4', '1', '6', '7', '4', '2', 'K', 'A', 'Z', 'B', 'W']
numbers = sorted([number for number in lst if number.isdigit()])
letters = sorted([letter for letter in lst if not letter.isdigit()], reverse=True)
combined = numbers + letters
print(combined)
Another way makes use of ord(...) and the ability to sort by tuples. Here we use zero for numbers and one for letters:
def sorter(item):
if item.isdigit():
return 0, int(item)
else:
return 1, -ord(item)
print(sorted(lst, key=sorter))
Both will yield
['1', '2', '2', '4', '4', '6', '7', 'Z', 'W', 'K', 'B', 'A']
As for timing:
def different_lists():
global my_list
numbers = sorted([number for number in my_list if number.isdigit()])
letters = sorted([letter for letter in my_list if not letter.isdigit()], reverse=True)
return numbers + letters
def key_function():
global my_list
def sorter(item):
if item.isdigit():
return 0, int(item)
else:
return 1, -ord(item)
return sorted(my_list, key=sorter)
from timeit import timeit
print(timeit(different_lists, number=10**6))
print(timeit(key_function, number=10**6))
This yields (running it a million times on my MacBook):
2.9208732349999997
4.54283629
So the approach with list comprehensions is faster here.
To elaborate on the custom-comparison approach: in Python the built-in sort does key comparison.
How to think about the problem: to group values and then sort each group by a different quality, we can think of "which group is a given value in?" as a quality - so now we are sorting by multiple qualities, which we do with a key that gives us a tuple of the value for each quality.
Since we want to sort the letters in descending order, and we can't "negate" them (in the arithmetic sense), it will be easiest to apply reverse=True to the entire sort, so we keep that in mind.
We encode: True for digits and False for non-digits (since numerically, these are equivalent to 1 and 0 respectively, and we are sorting in descending order overall). Then for the second value, we'll use the symbol directly for non-digits; for digits, we need the negation of the numeric value, to re-reverse the sort.
This gives us:
def custom_key(value):
numeric = value.isdigit()
return (numeric, -int(value) if numeric else value)
And now we can do:
my_list.sort(key=custom_key, reverse=True)
which works for me (and also handles multi-digit numbers):
>>> my_list
['1', '2', '2', '4', '4', '6', '7', 'Z', 'W', 'K', 'B', 'A']
You will have to implement your own comparison function and pass it as the key argument for the sorted function. What you are seeking is not a trivial comparison as you "assign" custom values to fields so you will have to let Python know how you value each one of them

parse sublists of a list in python

In python, I need to parse a list consisting of sublists. If the first elements of some sublists are the same, I need to pick the sublist with the least 4th element; but if the 4th elements are also the same, then I need select the sublist with higher 3rd element. For example, in the following list, I need to select sublists 1, 4 and 5.
alignments=[["A","B","10","4"],["A","C","15","8"],["A","E","20","10"],\
["D","C","15","3"],\
["G","U","1","9"],["G","O","10","9"]]
I achieved it with the code below which is very cumbersome:
best_alignments=[]
best_al=alignments[0]
k=0
c=0
counter_list=[]
for al in alignments[1:]:
c+=1
if best_al[0]==al[0]:
if best_al[3]==al[3]:
if best_al[2]<al[2]:
best_al=al
counter_list.append(c-1)
else:
counter_list.append(c)
else:
counter_list.append(c)
else:
if k==0:
best_al=al
k+=1
else:
best_al=al
for index in sorted(counter_list, reverse=True):
del alignments[index]
for el in alignments:
print(el)
I am sure there a much easier way to do that. Any suggestions are appreciated.
Here's a method that essentially does two passes over the data. First, it groups the data by the first item. Then, it returns the maximum as defined by your criteria, the least of the third element, and the most of the fourth (assuming you meant the integer value of the string).
from collections import defaultdict
def foo(alignments):
grouped = defaultdict(list)
for al in alignments:
grouped[al[0]].append(al)
return [
max(v, key=lambda al: (-int(al[2]),int(al[3])))
for v in grouped.values()
]
Pretty sure this is O(N) space and time, so not terribly inefficient.
In an Ipython repl:
In [3]: from collections import defaultdict
...: def foo(alignments):
...: grouped = defaultdict(list)
...: for al in alignments:
...: grouped[al[0]].append(al)
...: return [
...: max(v, key=lambda al: (-int(al[2]),int(al[3])))
...: for v in grouped.values()
...: ]
...:
In [4]: foo([['A', 'B', '10', '4'],
...: ['A', 'C', '15', '8'],
...: ['A', 'E', '20', '10'],
...: ['D', 'C', '15', '3'],
...: ['G', 'U', '1', '9'],
...: ['G', 'O', '10', '9']])
Out[4]: [['A', 'B', '10', '4'], ['D', 'C', '15', '3'], ['G', 'U', '1', '9']]
This is a sorted grouping where the sort order has multiple fields with different ascending/descending sequence. So you can sort the list in accordance with the fields and sequence, then pick the first occurrence of items based on the sublist's first element:
a = [["A","B","10","4"],["A","C","15","8"],["A","E","20","10"],
["D","C","15","3"],
["G","U","1","9"],["G","O","10","9"]]
seen = set()
sortKey = lambda sl: (sl[0],-int(sl[3]),sl[2])
first = lambda sl: sl[0] not in seen and not seen.add(sl[0])
result = [ sl for sl in sorted(a,key=sortKey) if first(sl) ]
print(result)
# [['A', 'E', '20', '10'], ['D', 'C', '15', '3'], ['G', 'U', '1', '9']]
This uses the sorted function's key parameter to produce a sorting order that will combine the 3 fields (reversing the order for the second sort field). Then filters the sorted list using a set to identify the first occurrence of the sublist's first field in each consecutive group.

Sort brackets after alphanumeric characters?

Working in Python 3:
a = ['(', 'z', 'a', '1', '{']
a.sort()
a
['(', '1', 'a', 'z', '{']
How can I sort the list so that alphanumeric characters come before punctuation characters:
a = ['(', 'z', 'a', '1', '{']
a.custom_sort()
a
['1', 'a', 'z', '(', '{']
(Actually I don't care about the order of the last two characters.)
This seems surprisingly difficult!
I understand that Python sorts asciibetically, and I'm looking for a human-readable sort. I found natsort but it only seems to deal with numbers.
You can use a key function for sort that returns a tuple to test if a given character is alphanumeric and use the character's lexicographical order as a secondary sorting key:
a.sort(key=lambda c: (not c.isalnum(), c))
You can pass a key function to sorted check if the value is in string.punctuation:
import string
punctuation = set(string.punctuation)
a = sorted(a, key=lambda x: (x in punctuation, x))
print(a)
#['1', 'a', 'z', '(', '{']
This approach explicitly checks if it's in the right sets:
import string
import sys
a = ['(', 'z', 'a', '1', '{']
def key(a):
if a in string.ascii_letters or a in string.digits:
return ord(a)
return sys.maxsize
a.sort(key=key)
print(a)

Categories