I want to create a dictionary out of a list that has several similar elements. But, in the dictionary, all these similar elements must have the same key.
d_dict={}
lst=['A1','A2','A3','2e','2o','2m']
for element in lst:
if element.startswith('A'):
d_dict[1].append(element)
elif element.startswith('2'):
d_dict[2].append(element)
print(d_dict)
My output should look like:
d_dict={1:['A1','A2','A3'],2:['2e','2o','2m']}
thanks.
You're looking for collections.defaultdict:
>>> from collections import defaultdict
>>> d_dict = defaultdict(list)
>>> for element in lst:
... if element.startswith('A'):
... d_dict[1].append(element)
... elif element.startswith('2'):
... d_dict[2].append(element)
...
>>> print d_dict
defaultdict(<type 'list'>, {1: ['A1', 'A2', 'A3'], 2: ['2e', '2o', '2m']})
So pretty much, with this module, your code is exactly the same. You only need to make your dictionary a type defaultdict so that you can have lists as values without having to create any.
You need to create the lists before appending something to them:
for element in lst:
if element.startswith('A'):
if 1 not in d_dict: # if it is not already created
d_dict[1] = [element] # add list with the current element
else:
d_dict[1].append(element)
elif element.startswith('2'):
if 2 not in d_dict:
d_dict[2] = [element]
else:
d_dict[2].append(element)
The only problem I see in your code is that you don't initialize the sublists, so d_dict[1] doesn't exist. But you can avoid having to do this altogether if you use a defaultdict.
from collections import defaultdict
d_dict=defaultdict(list)
d_dict[1].extend(e for e in lst if e.startswith('A'))
d_dict[2].extend(e for e in lst if e.startswith('2'))
If you wanted your code to be more flexible, this will work with any strings, not just those starting with A and 2. You can use the defaultdict class from collections.
from collections import defaultdict
values = ['A1','A2','A3','2e','2o','2m']
grouped = defaultdict(list)
for i in values:
grouped[i[0]].append(i)
print(dict(grouped))
Related
I get a list here:
my_list=["Alex:1990:London",
"Tony:1993:NYC",
"Kate:2001:Beijing",
"Tony:2001:LA",
"Alex:1978:Shanghai"]
How can I get the target dictionary my_target_dict from my_list in the easiest way?
my_target_dict={
"Alex":["Alex:1990:London", "Alex:1978:Shanghai"],
"Tony":["Tony:1993:NYC", "Tony:2001:LA"],
"Kate":["Kate:2001:Beijing"]
}
Use a defaultdict:
>>> from collections import defaultdict
>>> my_list=["Alex:1990:London", "Tony:1993:NYC", "Kate:2001:Beijing", "Tony:2001:LA", "Alex:1978:Shanghai"]
>>> d = defaultdict(list)
>>> for item in my_list:
... name, *_ = item.partition(":")
... d[name].append(item)
...
>>> d
defaultdict(<class 'list'>, {'Alex': ['Alex:1990:London', 'Alex:1978:Shanghai'], 'Tony': ['Tony:1993:NYC', 'Tony:2001:LA'], 'Kate': ['Kate:2001:Beijing']})
>>> d["Alex"]
['Alex:1990:London', 'Alex:1978:Shanghai']
You can use this comprehension to clean the list wrapped single items:
>>> {k:v if len(v) > 1 else v[0] for k,v in d.items()}
{'Alex': ['Alex:1990:London', 'Alex:1978:Shanghai'], 'Tony': ['Tony:1993:NYC', 'Tony:2001:LA'], 'Kate': 'Kate:2001:Beijing'}
In case you intend to work strictly with lists and dictionaries alone, try this:
my_target_dict=dict()
for value in my_list:
key=value.split(':')[0]
if key in my_target_dict:
my_target_dict[key].append(value)
else:
my_target_dict[key]=[value]
print(my_target_dict)
This is my solution for you:
my_list=["Alex:1990:London", "Tony:1993:NYC", "Kate:2001:Beijing", "Tony:2001:LA", "Alex:1978:Shanghai"]
dict = {}
for idx, content in enumerate(my_list):
name = content[:(content.index(':'))]
if name not in dict:
dict[name] = []
dict[name].append(my_list[idx])
First if you don't know about enumerate, it count your index and
take the content in each element of list.
Second, take name of there people by basic python of string. I use name = content[:(content.index(':'))] in order to take string from start to the first symbol ":".
Third, check if the key of dict exist or not. Otherwise, it will delete all your element in list of that key.
Last but not least, append the element you want into your key dict.
Your finally result:
{'Alex': ['Alex:1990:London', 'Alex:1978:Shanghai'], 'Tony': ['Tony:1993:NYC', 'Tony:2001:LA'], 'Kate': ['Kate:2001:Beijing']}
If you are a beginner (as I see) and don't want to use Python's collections module and do the implementation from scratch (it's imp to understand the concept of background work which collection does).
Once you are familiar with this, you can go with collections module and that is beautiful as it has many classes like defaultdict, OrderedDict etc. which can boost the speed of your work.
Here is what I have tried (do not forget to read the commented lines).
I have written a function named get_my_target_dict() which takes my_list and returns my_target_dict. And this is the modular implemenation (that you should prefer).
re is a module to work with regular expressions. Here it is used to match "Alex: 1990 : London" (i.e. spaces around :) kind of strings if any (by mistake).
import re
def get_my_target_dict(my_list):
my_target_dict = {} # dictionary
for string in my_list:
# "Alex:1990:London" => ["Alex", "1990", "London"]
# "Alex : 1990: London" => ["Alex", "1990", "London"]
items = re.split(r"\s*:\s*", string) # `\s*` is to match spaces around `:`
print(items)
# Alex, Tony etc.
key = items[0]
if key in my_target_dict:
my_target_dict[key].append(string)
else:
my_target_dict[key] = [string]
return my_target_dict
if __name__ == "__main__":
my_list=["Alex:1990:London",
"Tony:1993:NYC",
"Kate:2001:Beijing",
"Tony:2001:LA",
"Alex:1978:Shanghai"]
# Call get_my_target_dict(), pass my_list & get my_target_dict
my_target_dict = get_my_target_dict(my_list)
print(my_target_dict)
# {'Alex': ['Alex:1990:London', 'Alex:1978:Shanghai'], 'Tony': ['Tony:1993:NYC', 'Tony:2001:LA'], 'Kate': ['Kate:2001:Beijing']}
# Pretty printing dictionary
import json
print(json.dumps(my_target_dict, indent=4))
# {
# "Alex": [
# "Alex:1990:London",
# "Alex:1978:Shanghai"
# ],
# "Tony": [
# "Tony:1993:NYC",
# "Tony:2001:LA"
# ],
# "Kate": [
# "Kate:2001:Beijing"
# ]
# }
I want to make know if there is a command that can do this:
>>>A=dict()
>>>A[1]=3
>>>A
{1:3}
>>>A[1].add(5) #This is the command that I don't know if exists.
>>>A
{1:(3,5)}
I mean, add another value to the same key without quiting the old value added.
It is possible to do this?
You could make the dictionary values into lists:
>>> A = dict()
>>> A[1] = [3]
>>> A
{1: [3]}
>>> A[1].append(5) # Add a new item to the list
>>> A
{1: [3, 5]}
>>>
You may also be interested in dict.setdefault, which has functionality similar to collections.defaultdict but without the need to import:
>>> A = dict()
>>> A.setdefault(1, []).append(3)
>>> A
{1: [3]}
>>> A.setdefault(1, []).append(5)
>>> A
{1: [3, 5]}
>>>
A defaultdict of type list will create an empty list in case you access a key that does not exist in the dictionary so far. This often leads to quite elegant code.
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> d[1].append(3)
>>> d[1].append(2)
>>> d
defaultdict(<type 'list'>, {1: [3, 2]})
Using a defaultdict eliminates the "special case" of the initial insert.
from collections import defaultdict
A = defaultdict(list)
for num in (3,5):
A[1].append(num)
Like others pointed out, store the values in a list, but remember to check if the key is in the dictionary to determine whether you need to append or create a new list for that key...
A = dict()
if key in A: A[key].append(value)
else: A[key] = [value]
Say I have a list of list like this: (suppose you have no idea how many lists in this list)
list=[['food','fish'],['food','meat'],['food','veg'],['sports','football']..]
how can I merge the items in the list like the following:
list=[['food','fish','meat','veg'],['sports','football','basketball']....]
i.e, merge all the nested lists into the same list if they contain one of the same items.
Use defaultdict to make a dictionary that maps a type to values and then get the items:
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> items = [['food','fish'],['food','meat'],['food','veg'],['sports','football']]
>>> for key, value in items:
... d[key].append(value)
...
>>> [[key] + values for key, values in d.items()]
[['food', 'fish', 'meat', 'veg'], ['sports', 'football']]
The "compulsory" alternative to defaultdict which can work better for data that's already in order of the key and if you don't want to build data structures on it (ie, just work on groups)...
data = [['food','fish'],['food','meat'],['food','veg'],['sports','football']]
from itertools import groupby
print [[k] + [i[1] for i in v] for k, v in groupby(data, lambda L: L[0])]
But defaultdict is more flexible and easier to understand - so go with #Blender's answer.
Is there a way to have a defaultdict(defaultdict(int)) in order to make the following code work?
for x in stuff:
d[x.a][x.b] += x.c_int
d needs to be built ad-hoc, depending on x.a and x.b elements.
I could use:
for x in stuff:
d[x.a,x.b] += x.c_int
but then I wouldn't be able to use:
d.keys()
d[x.a].keys()
Yes like this:
defaultdict(lambda: defaultdict(int))
The argument of a defaultdict (in this case is lambda: defaultdict(int)) will be called when you try to access a key that doesn't exist. The return value of it will be set as the new value of this key, which means in our case the value of d[Key_doesnt_exist] will be defaultdict(int).
If you try to access a key from this last defaultdict i.e. d[Key_doesnt_exist][Key_doesnt_exist] it will return 0, which is the return value of the argument of the last defaultdict i.e. int().
The parameter to the defaultdict constructor is the function which will be called for building new elements. So let's use a lambda !
>>> from collections import defaultdict
>>> d = defaultdict(lambda : defaultdict(int))
>>> print d[0]
defaultdict(<type 'int'>, {})
>>> print d[0]["x"]
0
Since Python 2.7, there's an even better solution using Counter:
>>> from collections import Counter
>>> c = Counter()
>>> c["goodbye"]+=1
>>> c["and thank you"]=42
>>> c["for the fish"]-=5
>>> c
Counter({'and thank you': 42, 'goodbye': 1, 'for the fish': -5})
Some bonus features
>>> c.most_common()[:2]
[('and thank you', 42), ('goodbye', 1)]
For more information see PyMOTW - Collections - Container data types and Python Documentation - collections
Previous answers have addressed how to make a two-levels or n-levels defaultdict. In some cases you want an infinite one:
def ddict():
return defaultdict(ddict)
Usage:
>>> d = ddict()
>>> d[1]['a'][True] = 0.5
>>> d[1]['b'] = 3
>>> import pprint; pprint.pprint(d)
defaultdict(<function ddict at 0x7fcac68bf048>,
{1: defaultdict(<function ddict at 0x7fcac68bf048>,
{'a': defaultdict(<function ddict at 0x7fcac68bf048>,
{True: 0.5}),
'b': 3})})
I find it slightly more elegant to use partial:
import functools
dd_int = functools.partial(defaultdict, int)
defaultdict(dd_int)
Of course, this is the same as a lambda.
For reference, it's possible to implement a generic nested defaultdict factory method through:
from collections import defaultdict
from functools import partial
from itertools import repeat
def nested_defaultdict(default_factory, depth=1):
result = partial(defaultdict, default_factory)
for _ in repeat(None, depth - 1):
result = partial(defaultdict, result)
return result()
The depth defines the number of nested dictionary before the type defined in default_factory is used.
For example:
my_dict = nested_defaultdict(list, 3)
my_dict['a']['b']['c'].append('e')
Others have answered correctly your question of how to get the following to work:
for x in stuff:
d[x.a][x.b] += x.c_int
An alternative would be to use tuples for keys:
d = defaultdict(int)
for x in stuff:
d[x.a,x.b] += x.c_int
# ^^^^^^^ tuple key
The nice thing about this approach is that it is simple and can be easily expanded. If you need a mapping three levels deep, just use a three item tuple for the key.
How can I get a random pair from a dict? I'm making a game where you need to guess a capital of a country and I need questions to appear randomly.
The dict looks like {'VENEZUELA':'CARACAS'}
How can I do this?
One way would be:
import random
d = {'VENEZUELA':'CARACAS', 'CANADA':'OTTAWA'}
random.choice(list(d.values()))
EDIT: The question was changed a couple years after the original post, and now asks for a pair, rather than a single item. The final line should now be:
country, capital = random.choice(list(d.items()))
I wrote this trying to solve the same problem:
https://github.com/robtandy/randomdict
It has O(1) random access to keys, values, and items.
If you don't want to use the random module, you can also try popitem():
>> d = {'a': 1, 'b': 5, 'c': 7}
>>> d.popitem()
('a', 1)
>>> d
{'c': 7, 'b': 5}
>>> d.popitem()
('c', 7)
Since the dict doesn't preserve order, by using popitem you get items in an arbitrary (but not strictly random) order from it.
Also keep in mind that popitem removes the key-value pair from dictionary, as stated in the docs.
popitem() is useful to destructively iterate over a dictionary
>>> import random
>>> d = dict(Venezuela = 1, Spain = 2, USA = 3, Italy = 4)
>>> random.choice(d.keys())
'Venezuela'
>>> random.choice(d.keys())
'USA'
By calling random.choice on the keys of the dictionary (the countries).
Try this:
import random
a = dict(....) # a is some dictionary
random_key = random.sample(a, 1)[0]
This definitely works.
This works in Python 2 and Python 3:
A random key:
random.choice(list(d.keys()))
A random value
random.choice(list(d.values()))
A random key and value
random.choice(list(d.items()))
Since the original post wanted the pair:
import random
d = {'VENEZUELA':'CARACAS', 'CANADA':'TORONTO'}
country, capital = random.choice(list(d.items()))
(python 3 style)
If you don't want to use random.choice() you can try this way:
>>> list(myDictionary)[i]
'VENEZUELA'
>>> myDictionary = {'VENEZUELA':'CARACAS', 'IRAN' : 'TEHRAN'}
>>> import random
>>> i = random.randint(0, len(myDictionary) - 1)
>>> myDictionary[list(myDictionary)[i]]
'TEHRAN'
>>> list(myDictionary)[i]
'IRAN'
When they ask for a random pair here they mean a key and value.
For such a dict where the key:values are country:city,
use random.choice().
Pass the dictionary keys to this function as follows:
import random
keys = list(my_dict)
country = random.choice(keys)
You may wish to track the keys that were already called in a round and when getting a fresh country, loop until the random selection is not in the list of those already "drawn"... as long as the drawn list is shorter than the keys list.
Since this is homework:
Check out random.sample() which will select and return a random element from an list. You can get a list of dictionary keys with dict.keys() and a list of dictionary values with dict.values().
I am assuming that you are making a quiz kind of application. For this kind of application I have written a function which is as follows:
def shuffle(q):
"""
The input of the function will
be the dictionary of the question
and answers. The output will
be a random question with answer
"""
selected_keys = []
i = 0
while i < len(q):
current_selection = random.choice(q.keys())
if current_selection not in selected_keys:
selected_keys.append(current_selection)
i = i+1
print(current_selection+'? '+str(q[current_selection]))
If I will give the input of questions = {'VENEZUELA':'CARACAS', 'CANADA':'TORONTO'} and call the function shuffle(questions) Then the output will be as follows:
VENEZUELA? CARACAS
CANADA? TORONTO
You can extend this further more by shuffling the options also
With modern versions of Python(since 3), the objects returned by methods dict.keys(), dict.values() and dict.items() are view objects*. And hey can be iterated, so using directly random.choice is not possible as now they are not a list or set.
One option is to use list comprehension to do the job with random.choice:
import random
colors = {
'purple': '#7A4198',
'turquoise':'#9ACBC9',
'orange': '#EF5C35',
'blue': '#19457D',
'green': '#5AF9B5',
'red': ' #E04160',
'yellow': '#F9F985'
}
color=random.choice([hex_color for color_value in colors.values()]
print(f'The new color is: {color}')
References:
*Python 3.8: Standard Library Documentation - Built-in types: Dictionary view objects
Python 3.8: Data Structures - List Comprehensions:
I just stumbled across a similar problem and designed the following solution (relevant function is pick_random_item_from_dict; other functions are just for completeness).
import random
def pick_random_key_from_dict(d: dict):
"""Grab a random key from a dictionary."""
keys = list(d.keys())
random_key = random.choice(keys)
return random_key
def pick_random_item_from_dict(d: dict):
"""Grab a random item from a dictionary."""
random_key = pick_random_key_from_dict(d)
random_item = random_key, d[random_key]
return random_item
def pick_random_value_from_dict(d: dict):
"""Grab a random value from a dictionary."""
_, random_value = pick_random_item_from_dict(d)
return random_value
# Usage
d = {...}
random_item = pick_random_item_from_dict(d)
The main difference from previous answers is in the way we handle the dictionary copy with list(d.items()). We can partially circumvent that by only making a copy of d.keys() and using the random key to pick its associated value and create our random item.
Try this (using random.choice from items)
import random
a={ "str" : "sda" , "number" : 123, 55 : "num"}
random.choice(list(a.items()))
# ('str', 'sda')
random.choice(list(a.items()))[1] # getting a value
# 'num'
To select 50 random key values from a dictionary set dict_data:
sample = random.sample(set(dict_data.keys()), 50)
I needed to iterate through ranges of keys in a dict without sorting it each time and found the Sorted Containers library. I discovered that this library enables random access to dictionary items by index which solves this problem intuitively and without iterating through the entire dict each time:
>>> import sortedcontainers
>>> import random
>>> d = sortedcontainers.SortedDict({1: 'a', 2: 'b', 3: 'c'})
>>> random.choice(d.items())
(1, 'a')
>>> random.sample(d.keys(), k=2)
[1, 3]
I found this post by looking for a rather comparable solution. For picking multiple elements out of a dict, this can be used:
idx_picks = np.random.choice(len(d), num_of_picks, replace=False) #(Don't pick the same element twice)
result = dict ()
c_keys = [d.keys()] #not so efficient - unfortunately .keys() returns a non-indexable object because dicts are unordered
for i in idx_picks:
result[c_keys[i]] = d[i]
Here is a little Python code for a dictionary class that can return random keys in O(1) time. (I included MyPy types in this code for readability):
from typing import TypeVar, Generic, Dict, List
import random
K = TypeVar('K')
V = TypeVar('V')
class IndexableDict(Generic[K, V]):
def __init__(self) -> None:
self.keys: List[K] = []
self.vals: List[V] = []
self.dict: Dict[K, int] = {}
def __getitem__(self, key: K) -> V:
return self.vals[self.dict[key]]
def __setitem__(self, key: K, val: V) -> None:
if key in self.dict:
index = self.dict[key]
self.vals[index] = val
else:
self.dict[key] = len(self.keys)
self.keys.append(key)
self.vals.append(val)
def __contains__(self, key: K) -> bool:
return key in self.dict
def __len__(self) -> int:
return len(self.keys)
def random_key(self) -> K:
return self.keys[random.randrange(len(self.keys))]
b = { 'video':0, 'music':23,"picture":12 }
random.choice(tuple(b.items())) ('music', 23)
random.choice(tuple(b.items())) ('music', 23)
random.choice(tuple(b.items())) ('picture', 12)
random.choice(tuple(b.items())) ('video', 0)