Reduce the key value of dictionary by one - python

Having a dictionary as below:
a_dict = {1: 'blue', 2: 'apple', 3: 'dog'}
need to reduce the key value by one and drop the blue value.
output:
a_dict = {1: 'apple', 2: 'dog'}

What you want to do is a bit strange (what is the real underlying goal?)
One option, assuming you want to keep the same order, and shift the values after blue to one key before:
l = list(a_dict.values())
l.remove('blue')
d = dict(zip(a_dict, l))
Output: {1: 'apple', 2: 'dog'}
NB. In case of multiple 'blue', this would only remove the first one. To remove all:
d = dict(zip(a_dict, [v for v in a_dict.values() if v != 'blue']))
dropping the first value
If you already know that the value to drop if the first one:
out = dict(zip(a_dict, list(a_dict.values())[1:]))
Or, more efficient:
i = iter(a_dict.values())
next(i) # consume first value
out = dict(zip(a_dict, i))

Another solution, with := operator:
a_dict = {1: "blue", 2: "apple", 3: "dog"}
i = 0
a_dict = {i: v for v in a_dict.values() if v != "blue" and (i := i + 1)}
print(a_dict)
Prints:
{1: 'apple', 2: 'dog'}

Related

Set value as key and a list of values as value in Python

I have a big dictionary (250k+ keys) like this:
dict = {
0: [apple, green],
1: [banana, yellow],
2: [apple, red],
3: [apple, brown],
4: [kiwi, green],
5: [kiwi, brown],
...
}
Goal to achieve:
1. I want a new dictionary with the first value of the list as key, and a list of values for the same key. Something like this:
new_dict = {
apple: [green, red, brown]
banana: [yellow]
kiwi: [green, brown],
...
}
2. After that I want to count the number of values for each key (e.g. {apple:3, banana:1, kiwi,2} ), and this could be easily achieved with a Counter, so it shouldn't be a problem.
Then, I want to select only the keys that have a certain number of values (for example, if I want to mantain only keys associated to 2 or more values, the final_dict will be this:
final_dict = {
apple:3,
kiwi:2,
....
}
3. Then I want to return the original keys from dict of the elements that have at least 2 values, so at the end I will have:
original_keys_with_at_least_2_values = [0, 2, 3, 4, 5]
My code
# Create new_dict like: new_dict = {apple:None, banana:None, kiwi:None,..}
new_dict = {k: None for k in dict.values()[0]}
for k in new_dict.keys():
for i in dict.values()[0]:
if i == k:
new_dict[k] = dict[i][1]
I'm stuck using nested for cicles, even if I know Python comprehension is faster, but I really don't know how to solve it. Any solution or idea would be appreciated.
You can use a defaultdict to group the items by the first entry
from collections import defaultdict
fruits = defaultdict(list)
data = {
0: ['apple', 'green'],
1: ['banana', 'yellow'],
2: ['apple', 'red'],
3: ['apple', 'brown'],
4: ['kiwi', 'green'],
5: ['kiwi', 'brown']
}
for _, v in data.items():
fruits[v[0]].extend(v[1:])
print(dict(fruits))
# {'apple': ['green', 'red', 'brown'], 'banana': ['yellow'], 'kiwi': ['green', 'brown']}
If there is less than two entries in any list, you'll need to account for that...
Then, use comprehension to get the counts, not Counter as that won't give you the lengths of those lists.
fruits_count = {k: len(v) for k, v in fruits.items()}
fruits_count_with_at_least_2 = {k: v for k, v in fruits_count.items() if v >= 2}
And then a loop will be needed to collect the original keys
original_keys_with_2_count = []
for k, values in data.items():
fruit = values[0]
count = fruits_count.get(fruit, -1)
if count >= 2:
original_keys_with_2_count.append(k)
print(original_keys_with_2_count)
# [0, 2, 3, 4, 5]

How to convert dict of dict to dict of specified format?

I have a dictionary of dictionaries as shown below:
d = {0: {1: ["hello"], 2: ["How are you"]}, 1: {1: ["!"], 2: ["?"]}}
and I would want it be in required format:
result = {1:["hello", "!"], 2: ["How are you", "?"]}
However, I get this in the following format using the code below:
new_d = {}
for sub in d.values():
for key, value in sub.items():
new_d.setdefault(key, []).append(value)
The result is not of required structure and it causes a list of lists.
{1: [['hello'], ['!']], 2: [['How are you'], ['?']]}
Any help here would be highly appreciated. Thanks.
use extend instead of append:
new_d.setdefault(key, []).extend(value)
The extend() method adds all the elements of an iterable (list, tuple, string etc.) to the end of the list.
If you want to solve this problem with using append() function try this code:
new_d = {}
for sub in d.values():
for key, value in sub.items():
# Control key exist...
if(key in new_d.keys()):
new_d[key].append(value[0])
else:
new_d[key] = value
You can either use .extend(value) instead of .append(value)
or you can add a basic for loop to flatten the list of all dictionary values as shown below.
new_d = {}
for sub in d.values():
for key, value in sub.items():
new_d.setdefault(key, []).extend(value)
for i in range (0,len(d)):
new_d[i+1] = [item for sublist in new_d.get(i+1) for item in sublist]
print(new_d)
The accepted answer by #Gabip correctly identifies that your only mistake was using append instead of extend.
That mistake being corrected, I'd also like to suggest a slightly different approach using dict comprehensions:
d = {0: {1: ["hello"], 2: ["How are you"]}, 1: {1: ["!"], 2: ["?"]}}
new_d = {key: d[0].get(key, []) + d[1].get(key, []) for key in d[0]}
# {1: ['hello', '!'], 2: ['How are you', '?']}
Or a more robust version that takes keys from both d[0] and d[1], in case some keys are in d[1] but not in d[0]:
d = {0: {1: ["hello"], 2: ["How are you"]}, 1: {1: ["!"], 2: ["?"], 3: ['>>>']}}
new_d = {key: d[0].get(key, []) + d[1].get(key, []) for key in set(d[0].keys()) | set(d[1].keys())}
# {1: ['hello', '!'], 2: ['How are you', '?'], 3: ['>>>']}
Finally, this wasn't explicitly part of your question, but I suggest using str.join to join the strings:
d = {0: {1: ["hello"], 2: ["How are you"]}, 1: {1: ["!"], 2: ["?"]}}
new_d = {key: ''.join(d[0].get(key, []) + d[1].get(key, [])) for key in d[0]}
# {1: 'hello!', 2: 'How are you?'}

Python dictionary print all values for all keys

I have two python lists:
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
The "keys" are cluster ID list for the corresponding words in "values" list. I wish to print key-value pairs using
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
dictionary = dict(zip(keys, values))
for key, value in dictionary.items() :
print (key, value)
But it only prints
1 apple
2 paper
3 tennis
What I actually want is to get all values for all keys like this
1 [apple]
2 [book,pen,paper]
3 [soccer,tennis]
I know that my current code should logically print the first output as keys are unique. But how can I change it so that it will print all values for all keys? Thank you in advance!
from collections import defaultdict
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
d = defaultdict(list)
for k, v in zip(keys, values):
d[k].append(v)
Looks like what you want is a mapping from one key to multiple values, one way to accomplish it would be:
from collections import defaultdict
d = defaultdict(list)
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
for tuple in zip(keys, values):
d[tuple[0]].append(tuple[1])
print(d) # defaultdict(<class 'list'>, {1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']})
You can use itertools:
import itertools
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
final_data = {a:[i[0] for i in b] for a, b in [(a, list(b)) for a, b in itertools.groupby(sorted(zip(values, keys), key=lambda x:x[-1]), key=lambda x:x[-1])]}
Output:
{1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']}
pure python also works
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
d = dict(zip(keys, [[] for _ in keys])) # dict w keys, empty lists as values
for k, v in zip(keys, values):
d[k].append(v)
d
Out[128]: {1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']}
Two method :
If you want you can use default dict as many already have been suggested :
Data is :
keys=[1,2,2,3,2,3]
values=['apple','book','pen','soccer','paper','tennis']
Method: 1
import collections
d=collections.defaultdict(list)
for i in zip(keys,values):
d[i[0]].append(i[1])
print(d)
output:
{1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']}
Or if you want to develop your own logic without importing any external module then you can try:
result={}
for i in zip(keys,values):
if i[0] not in result:
result[i[0]]=[i[1]]
else:
result[i[0]].append(i[1])
print(result)
output:
{1: ['apple'], 2: ['book', 'pen', 'paper'], 3: ['soccer', 'tennis']}

A better way to rename keys of a dictionary after deletion of an item?

I have a dictionary that I am using. I occasionally delete values from it and then have to go back through and rename the keys. I am accomplishing the renaming like so:
TestDic = {0: "Apple", 2: "Orange", 3: "Grape"}
print(TestDic)
TempDic = {}
i = 0
for Key, DictValue in TestDic.iteritems():
TempDic[i] = DictValue
i += 1
TestDic= TempDic
print(TestDic)
Outputs:
{0: 'Apple', 1: 'Orange', 2: 'Grape'}
Great. Now is there a better way? I saw this, but I cannot pop off the old key, as the old key/value pair are gone. And this deals with reformatting the int/floats in the dictionary.
Use a list instead. If your keys are sequential integers, referencing elements will be the same anyway, and you won't have to mess about renaming keys:
>>> data = ["Apple", "Gooseberry", "Orange", "Grape"]
>>> data[0]
'Apple'
>>> data[1]
'Gooseberry'
>>> data[2]
'Orange'
>>> data[3]
'Grape'
>>> data.remove("Gooseberry")
>>> data
['Apple', 'Orange', 'Grape']
>>> data[0]
'Apple'
>>> data[1]
'Orange'
>>> data[2]
'Grape'
>>>
If you really want to stick with using a dictionary, you could do what you want like this, which doesn't require creating a temporary dictionary (although it does create a temporary list):
testdic = {0: "Apple", 1: "Blueberry", 2: "Orange", 3: "Grape"}
print(testdic)
delkey = 1 # key of item to delete
del testdic[delkey]
print(testdic)
# go through dict's items and renumber those affected by deletion
for key, value in testdic.iteritems():
if key > delkey: # decrement keys greater than the key deleted
testdic[key-1] = value
del testdic[key]
print(testdic)
Output:
{0: 'Apple', 1: 'Blueberry', 2: 'Orange', 3: 'Grape'}
{0: 'Apple', 2: 'Orange', 3: 'Grape'}
{0: 'Apple', 1: 'Orange', 2: 'Grape'}

counting incidences of a number and associated value in two synchronized lists

I have two lists that are of the same length:
alist = ['XX', 'HH', 'GG', 'XX', 'II', 'PP', 'LL', 'TT', 'KK', 'XX']
blist = [2, 3, 5, 5, 9, 8, 9, 4, 7, 2]
I want to know what values in alist have the same number in blist. I want the outcome to look like this:
2 = XX; 3 = HH; 5 = GG, XX; 9 = II, LL; 8 = PP; 4 = TT; 7 = KK
I solved it like this:
from collections import defaultdict
adict = {}
a = zip(blist, alist)
for key, value in a:
adict.setdefault(k, []).append(v)
which gives this result:
adict:
{2: ['XX', 'XX'], 3: ['HH'], 4: ['TT'], 5: ['GG', 'XX'], 7: ['KK'], 8: ['PP'], 9: ['II', 'LL']}
but I dont want the same value twice, for example 2: ['XX', 'XX'] - I would like to have instead 2: ['XX'].
I tried this using 'set' before the list of values:
a = zip(blist, alist)
for key, value in a:
a.setdefault(k, set[]).append(v)
but it complained...
any ideas?
I'd personally use Martijn's defaultdict approach, but I thought I'd address the issues with your current attempt.
The problem with your code:
a = zip(blist, alist)
for key, value in a:
a.setdefault(k, set[]).append(v)
Is That:
after the zip, a is now a list, so it won't support .setdefault
k is not used in the for loop - it should be key
v is not used in the for loop = it should be value
set[] is invalid synatx - to create a new set - use set()
A set does not have an .append - instead you want to use add
Corrected code:
d = {}
a = zip(blist, alist)
for key, value in a:
d.setdefault(key, set()).add(value)
Adapt the following as desired to print:
for k, v in d.iteritems():
if k > 5:
print '{0}: {1}'.format(k, ','.join(v))
Use the defaultdict type you imported but otherwise ignore:
from collections import defaultdict
a = defaultdict(set)
for k, v in zip(blist, alist):
a[k].add(v)
results in:
>>> a
defaultdict(<type 'set'>, {2: set(['XX']), 3: set(['HH']), 4: set(['TT']), 5: set(['GG', 'XX']), 7: set(['KK']), 8: set(['PP']), 9: set(['II', 'LL'])})
defaultdict is a subclass of dict and behaves in the same way otherwise.
Use your code and then add:
a = {x: set(a[x]) for x in a }

Categories