Python 2.7.9 dictionary find and delete - python

Python 2.7.9 dictionary question:
I have a dictionary in Python that contain lists that have been appended previously, and these lists are mapped, e.g. 1=>10.2, 2=>10.33
How may I find a single value within the dictionary and delete it?
E.g. find 'a'=2 and delete 'a' and corresponding 'b' value:
myDictBefore = {'a': [1, 2, 3], 'b': [10.2, 10.33, 10.05]}
myDictAfter = {'a': [1, 3], 'b': [10.2, 10.05]}
I suspect I should find 'a' value and get the index and then
delete myDict['a'][index]
and myDict['b'][index] - though I'm unsure how to do this.

How about:
idx = myDictBefore['a'].index(2)
myDictBefore['a'].pop(idx)
myDictBefore['b'].pop(idx)
If this comes up more often, you might as well write a general function for it:
def removeRow(dct, col, val):
'''remove a "row" from a table-like dictionary containing lists,
where the value of that row in a given column is equal to some
value'''
idx = dct[col].index(val)
for key in dct:
dct[key].pop(idx)
which you could then use like this:
removeRow(myDictBefore, 'a', 2)

You could define a function that does it.
def remove(d, x):
index = d['a'].index(x) # will raise ValueError if x is not in 'a' list
del d['a'][index]
del d['b'][index]
myDict = {'a': [1, 2, 3], 'b': [10.2, 10.33, 10.05]}
remove(myDict, 2)
print(myDict) # --> {'a': [1, 3], 'b': [10.2, 10.05]}

Related

Combining a nested list without affecting the key and value direction in python

I have a program that stores data in a list. The current and desired output is in the format:
# Current Input
[{'Devices': ['laptops', 'tablets'],
'ParentCategory': ['computers', 'computers']},
{'Devices': ['touch'], 'ParentCategory': ['phones']}]
# Desired Output
[{'Devices': ['laptops', 'tablets','touch'],
'ParentCategory': ['computers', 'computers','phones']}]
Can you give me an idea on how to combine the lists with another python line of code or logic to get the desired output?
You can do something like this:
def convert(a):
d = {}
for x in a:
for key,val in x.items():
if key not in d:
d[key] = []
d[key] += val
return d
Code above is for Python 3.
If you're on Python 2.7, then I believe that you should replace items with iteritems.
Solution using a dict comprehension: build the merged dictionary first by figuring out which keys it should have, then by concatenating all the lists for each key. The set of keys, and each resulting list, are built using itertools.chain.from_iterable.
from itertools import chain
def merge_dicts(*dicts):
return {
k: list(chain.from_iterable( d[k] for d in dicts if k in d ))
for k in set(chain.from_iterable(dicts))
}
Usage:
>>> merge_dicts({'a': [1, 2, 3], 'b': [4, 5]}, {'a': [6, 7], 'c': [8]})
{'a': [1, 2, 3, 6, 7], 'b': [4, 5], 'c': [8]}
>>> ds = [
{'Devices': ['laptops', 'tablets'],
'ParentCategory': ['computers', 'computers']},
{'Devices': ['touch'],
'ParentCategory': ['phones']}
]
>>> merge_dicts(*ds)
{'ParentCategory': ['computers', 'computers', 'phones'],
'Devices': ['laptops', 'tablets', 'touch']}

how to get all the values in a dictionary with the same key? [duplicate]

I want to add multiple values to a specific key in a python dictionary. How can I do that?
a = {}
a["abc"] = 1
a["abc"] = 2
This will replace the value of a["abc"] from 1 to 2.
What I want instead is for a["abc"] to have multiple values (both 1 and 2).
Make the value a list, e.g.
a["abc"] = [1, 2, "bob"]
UPDATE:
There are a couple of ways to add values to key, and to create a list if one isn't already there. I'll show one such method in little steps.
key = "somekey"
a.setdefault(key, [])
a[key].append(1)
Results:
>>> a
{'somekey': [1]}
Next, try:
key = "somekey"
a.setdefault(key, [])
a[key].append(2)
Results:
>>> a
{'somekey': [1, 2]}
The magic of setdefault is that it initializes the value for that key if that key is not defined. Now, noting that setdefault returns the value, you can combine these into a single line:
a.setdefault("somekey", []).append("bob")
Results:
>>> a
{'somekey': [1, 2, 'bob']}
You should look at the dict methods, in particular the get() method, and do some experiments to get comfortable with this.
How about
a["abc"] = [1, 2]
This will result in:
>>> a
{'abc': [1, 2]}
Is that what you were looking for?
Append list elements
If the dict values need to be extended by another list, extend() method of lists may be useful.
a = {}
a.setdefault('abc', []).append(1) # {'abc': [1]}
a.setdefault('abc', []).extend([2, 3]) # a is now {'abc': [1, 2, 3]}
This can be especially useful in a loop where values need to be appended or extended depending on datatype.
a = {}
some_key = 'abc'
for v in [1, 2, 3, [2, 4]]:
if isinstance(v, list):
a.setdefault(some_key, []).extend(v)
else:
a.setdefault(some_key, []).append(v)
a
# {'abc': [1, 2, 3, 2, 4]}
Append list elements without duplicates
If there's a dictionary such as a = {'abc': [1, 2, 3]} and it needs to be extended by [2, 4] without duplicates, checking for duplicates (via in operator) should do the trick. The magic of get() method is that a default value can be set (in this case empty set ([])) in case a key doesn't exist in a, so that the membership test doesn't error out.
a = {some_key: [1, 2, 3]}
for v in [2, 4]:
if v not in a.get(some_key, []):
a.setdefault(some_key, []).append(v)
a
# {'abc': [1, 2, 3, 4]}

Merging dictionaries

I need to append the values from one dictionary (N) to another (M) - pseudocode below
if[x] in M:
M[x]=M[x]+N[x]
else:
M[x]=N[x]
Doing this for every key in N seems quite untidy coding.
What would be the most efficient way to achieve this?
Of course you should be iterating your keys in "x" already - but a single line solution is:
M.update({key:((M[key] + value) if key in M else value) for key, value in N.items()})
not entirely sure what your x is (guessing the keys of both M and N), then this might work:
M = {key: M.get(key, 0) + N.get(key, 0) for key in set((*M, *N))}
for the example:
M = {'a': 1, 'c': 3, 'e': 5}
N = {'a': 2, 'b': 4, 'c': 6, 'd': 8}
you get:
print(M) # {'a': 3, 'e': 5, 'd': 8, 'c': 9, 'b': 4}
or please clarify what the desired output for the given example would be.
When you say "append", I assume that means that the values in your dicts are lists. However the techniques below can easily be adapted if they're simple objects like integers or strings.
Python doesn't provide any built-in methods for handling dicts of lists, and the most efficient way to do this depends on the data, because some ways work best when there are a high proportion of shared keys, other ways are better when there aren't many shared keys.
If the proportion of shared keys isn't too high, this code is reasonably efficient:
m = {1:[1, 2], 2:[3]}
n = {1:[4], 3:[5]}
for k, v in n.items():
m.setdefault(k, []).extend(v)
print(m)
output
{1: [1, 2, 4], 2: [3], 3: [5]}
You can make this slightly faster by caching the .setdefault method and the empty list:
def merge(m, n):
setdef = m.setdefault
empty = []
for k, v in n.items():
setdef(k, empty).extend(v)
If you expect a high proportion of shared keys, then it's better to perform set operations on the keys (in Python 3, dict.keys() return a set-like View object, which is extremely efficient to construct), and handle the shared keys separately from the unique keys of N.

how to use a sentinel list in a comprehension?

I have a list
In [4]: a = [1, 2, 3, 3, 2, 4]
from which I would like to remove duplicates via a comprehension using a sentinel list (see below why):
In [8]: [x if x not in seen else seen.append(x) for x in a]
Out[8]: [1, 2, 3, 3, 2, 4]
It seems that seen is not taken into account (neither updated, not checked). Why is it so?
As for the reason why using a convoluted method: The list I have is of the form
[{'a': 3, 'b': 4}, {'a': 10, 'b': 4}, {'a': 5, 'b': 5}]
and I want to remove duplicates based on the value of a specific key (b in the case above, to leave [{'a': 3, 'b': 4}, {'a': 5, 'b': 5}] (I do not care which dict is removed). The idea would be to build a sentinel list with the values of b and keep only the dicts without b equal to any element in that sentinel list.
Since x is not in seen, you are never adding it to seen either; the else branch is not executed when x not in seen is true.
However, you are using a conditional expression; it always produces a value; either x or the result of seen.append() (which is None), so you are not filtering, you are mapping here.
If you wanted to filter, move the test to an if section after the for loop:
seen = set()
[x for x in a if not (x in seen or seen.add(x))]
Since you were using seen.append() I presume you were using a list; I switched you to a set() instead, as membership tests are way faster using a set.
So x is excluded only if a) x in seen is true (so we have already seen it), or seen.append(x) returned a true value (None is not true). Yes, this works, if only a little convoluted.
Demo:
>>> a = [1, 2, 3, 3, 2, 4]
>>> seen = set()
>>> [x for x in a if not (x in seen or seen.add(x))]
[1, 2, 3, 4]
>>> seen
set([1, 2, 3, 4])
Applying this to your specific problem:
>>> a = [{'a': 3, 'b': 4}, {'a': 10, 'b': 4}, {'a': 5, 'b': 5}]
>>> seen = set()
>>> [entry for entry in a if not (entry['b'] in seen or seen.add(entry['b']))]
[{'a': 3, 'b': 4}, {'a': 5, 'b': 5}]
You never execute the else part of the if, because you do not update when you match the first time. You could do this:
[seen.append(x) or x for x in lst if x not in seen]
This way the or returns the last value (and executes the update using append (which always returns None, to let the or continue looking for truth-y value).
Maybe you can use the fact that dict keys are a set for this. If you want to prioritize the last items use reversed (last item is prioritized here):
>>> lst = [{'a': 3, 'b': 4}, {'a': 10, 'b': 4}, {'a': 5, 'b': 5}]
>>> filtered = {item['b']: item for item in reversed(lst)}
>>> filtered.values()
[{'a': 3, 'b': 4}, {'a': 5, 'b': 5}]
This uses 'b' as the key to map a value to, so only a single elemnt can be mapped to a value of 'b', which effectively creates a set over 'b'.
note: this will return the values in random order. To fix it nicely, for big datasets, I'd create another mapping, of each object to it's index in the original list (O(n)), and use that mapping as a sorting function of the final result (O(n*log(n))). That's beyond the scope of this answer.
I'm always queasy making use of operator precedence as execution flow control. I feel that the below is marginally more explicit and palatable, although it does carry the additional cost of tuple creation.
b_values = set()
[(item, b_values.add(item['b']))[0] for item in original_list
if item['b'] not in b_values]
But really when you're maintaining/updating some sort of state, I think the best format is the simple for-loop:
output_list = []
b_values = set()
for item in original_list:
if item['b'] not in b_values:
output_list.append(item)
b_values.add(item['b'])

validating a dictionary with lists that it all contains the same length

I have a dictionary which looks like this
dic = {'x': [1, 2], 'z': [1, 3], 'r': [4, 5]}
Am trying to figure out a way to check that all the values in the dictionary have the same list length.
I tried the following
if (len(dic['x']) == len(dic['r']) == len(dic['z'])):
this works, but am looking for a way todo it without knowing the keys of the dictionary as they may vary.. any one can help?
Try something like this:
vals = dic.values()
l = len(vals[0])
if all(len(item) == l for item in vals):
# ok
Or, perhaps this:
lens = map(len, dic.values())
if len(set(lens)) == 1:
# ok
This will work on both Python2 and 3:
>>> dic = {'x': [1, 2], 'z': [1, 3], 'r': [4, 5]}
>>> f = len(dic[next(iter(dic))])
>>> all(len(x) == f for x in dic.values())
True
If number of items are huge then it's better to iterate over just dict itself, as dic.values() will return a list of all values in Python2(You can use dict.itervalues to get an iterator in Python2).:
>>> all(len(dic[k]) == f for k in dic)
True

Categories