Using set on Dictionary keys - python

For my program, I wish to cleanly check whether any elements in a list is a key in a dictionary. So far, I can only think to loop through the list and checking.
However, is there any way to simplify this process? Is there any way to use sets? Through sets, one can check whether two lists have common elements.

This should be easy using the builtin any function:
any(item in dct for item in lst)
This is quick, efficient and (IMHO) quite readible. What could be better? :-)
Of course, this doesn't tell you which keys are in the dict. If you need that, then you're best recourse is to use dictionary view objects:
# python2.7
dct.viewkeys() & lst # Returns a set of the overlap
# python3.x
dct.keys() & lst # Same as above, but for py3.x

You can test for an intersection between the dictionary's keys and the list items using dict.keys:
if the_dict.keys() & the_list:
# the_dict has one or more keys found in the_list
Demo:
>>> the_dict = {'a':1, 'b':2, 'c':3}
>>> the_list = ['x', 'b', 'y']
>>> if the_dict.keys() & the_list:
... print('found key in the_list')
...
found key in the_list
>>>
Note that in Python 2.x, the method is called dict.viewkeys.

As far as efficiency goes, you can't be any more efficient than looping through the list. I would also argue that looping through the list is already a simple process.

Related

python OrderedDict get a key index of

Can OrderedDict get a key position?
is like list of index()
test = ['a', 'b', 'c', 'd', 'e']
test.index('b') # return 1
just one line program.
such as:
print(list(your_ordered_dict).index('your_key'))
Maybe you can use lambda,like this line program:
f = lambda ordered_dict, key: list(ordered_dict).index(key)
Good luck.
Keep it simple.
from collections import OrderedDict
x = OrderedDict('test1'='a', 'test2'='b')
print(list(x.keys().index('test1'))
You can write this in two ways:
list(x).index('b')
next(i for i, k in enumerate(x) if k=='b')
The first one will be a little faster for small dicts, but a lot slower, and waste a lot of space, for huge ones. (Of course most of the time, OrderedDicts are pretty small.)
Both versions will work for any iterable; there's nothing special about OrderedDict here.
If you take the keys as a list, you can then index like:
Code:
list(x).index('b')
Test Code:
from collections import OrderedDict
x = OrderedDict(a=1, b=2)
print(list(x).index('b'))
Results:
1
The accepted answer list(x).index('b') will be O(N) every time you're searching for the position.
Instead, you can create a mapping key -> position which will be O(1) once the mapping is constructed.
ordered_dict = OrderedDict(a='', b='')
key_to_pos = {k: pos for pos, k in enumerate(ordered_dict)}
assert key_to_pos['b'] == 1

Return the smallest element value in a list of lists

Given a list of lists like this :
[["fileA",7],["fileB",4],["fileC",17],["fileD",15]]
How would you return the first element associated to the smallest value ? In this instance "fileB" since it has the smallest value (4). I'm guessing the shortest way would be using list comprehension .
Actually, a list comprehension wouldn't be the best tool for this. Instead, you should use min, its key function, and operator.itemgetter:
>>> from operator import itemgetter
>>> lst = [["fileA",7],["fileB",4],["fileC",17],["fileD",15]]
>>> min(lst, key=itemgetter(1))
['fileB', 4]
>>> min(lst, key=itemgetter(1))[0]
'fileB'
>>>
Without importing anything you can do:
min(lst, key = lambda x: x[1])[0]
I came up with this weird idea which doesn't use anything but simple generator expression:
min((x[1], x[0]) for x in lst)[1]
It is based on the fact that the minimization is done on the first element of a tuple/list by default.
You should probably just convert the data to a dictionary, since that immediately seems a lot more sensible than having a list of lists. Then you can manipulate and access your data more easily.
myLists = [["fileA",7],["fileB",4],["fileC",17],["fileD",15]]
myDict = dict(myLists)
min(myDict, key=myDict.get)
# 'fileB'

Why can't I iterate over a Counter in Python?

Why is that when I try to do the below, I get the need more than 1 value to unpack?
for key,value in countstr:
print key,value
for key,value in countstr:
ValueError: need more than 1 value to unpack
However this works just fine:
for key,value in countstr.most_common():
print key,value
I don't understand, aren't countstr and countstr.most_common() equivalent?
EDIT:
Thanks for the below answers, then I guess what I don't understand is: If countstr is a mapping what is countstr.most_common()? -- I'm really new to Python, sorry if I am missing something simple here.
No, they're not. Iterating over a mapping (be it a collections.Counter or a dict or ...) iterates only over the mapping's keys.
And there's another difference: iterating over the keys of a Counter delivers them in no defined order. The order returned by most_common() is defined (sorted in reverse order of value).
No, they aren't equivalent. countstr is a Counter which is a dictionary subclass. Iterating over it yields 1 key at a time. countstr.most_common() is a list which contains 2-tuples (ordered key-value pairs).
A countstr is a Counter which is a subclass for counting hashable objects. It is an unordered collection where elements are stored as dictionary keys and their counts are stored as dictionary values.
>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> list(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']
You can't iterate the counter values/keys directly but you can copy them into a list and iterate the list.
s = ["bcdef", "abcdefg", "bcde", "bcdef"]
import collections
counter=collections.Counter(s)
vals = list(counter.values())
keys = list(counter.keys())
vals[0]
keys[0]
Output:
2
'bcdef'

How risky is using list.index() for getting the index of a set?

I want to find and store the index of an element in a set of strings. Basically, I have a set of strings. If a new string lies in that set, I want it to be allotted the index number. If not, I want to add it to the set, and allot a new index number.
Now, I'm pretty sure I can do this using a dictionary with ease. I was wondering whether I could do the following, however:
s = set(['apple','banana','orange','mango','pineapple'])
if fruit in s:
print list(s).index(fruit)
else:
s.add(fruit)
print list(s).index(fruit)
Will the order of existing elements change if a new element is added, or a new instance of an existing element is added? I've tried it out and it doesn't seem to change, but I wanted to be sure.
Thanks.
It's a bad idea to use indexing on sets. Sets are unordered collection.
You could use a combination of set and list here. Set to keep track of seen items and list to maintain the order of insertion.
>>> lis = []
>>> seen = set()
>>> for x in ['apple','banana','orange']:
if x not in seen:
seen.add(x)
lis.append(x)
...
>>> if 'banana' in seen:
... print lis.index('banana')
... else:
... seen.add('banana')
... lis.append('banana')
... print len(lis)-1
...
1
>>> if 'bar' in seen:
print lis.index('bar')
else:
seen.add('bar')
lis.append('bar')
print len(lis)-1
...
3
Risky:
Order is intact:
>>> lis
['apple', 'banana', 'orange', 'bar']
list maintains the order, while list(set) returns items in arbitrary order.
>>> list(seen)
['orange', 'bar', 'apple', 'banana']
Will the order of existing elements change if a new element is added[?]
It can easily change. sets don't have order, and you can't rely on them being ordered by insertion in practice, either:
>>> a = {"b", "c"}
>>> a
set(['c', 'b'])
>>> list(a).index("c")
0
>>> a.add("a")
>>> a
set(['a', 'c', 'b'])
>>> list(a).index("c")
1
Sets are unordered collections, if you build a list from a set you cannot be sure that the elements in the list will always have the same order. It may happen, but it's not guaranteed.

Create a list from a list of dictionaries using comprehension for specific key value

How can I write a comprehension to extract all values of key='a'?
alist=[{'a':'1a', 'b':'1b'},{'a':'2a','b':'2b'}, {'a':'3a','b':'3b'}]
The following works but I just hacked until I got what I want. Not a good way to learn.
[alist['a'] for alist in alist if 'a' in alist]
in the comprehension I have been trying to use if key='a' in alist else 'No data'
[elem['a'] for elem in alist if 'a' in elem]
might be a clearer way of phrasing what you have above.
The "for elem in alist" part will iterate over alist, allowing this to look through each dictionary in alist.
Then, the "if 'a' in elem" will ensure that the key 'a' is in the dictionary before the lookup occurs, so that you don't get a KeyError from trying to look up an element that doesn't exist in the dictionary.
Finally, taking elem['a'] gives you the value in each dictionary with key 'a'. This whole statement will then give the list of values in each of the dictionaries with key 'a'.
Hope this makes it a bit clearer.
You can do:
alist=[{'a':'1a', 'b':'1b'},{'a':'2a','b':'2b'}, {'a':'3a','b':'3b'}]
new_list = [a.get('a') for a in alist]
If you want to restrict it only to dictionary with a key a,
new_list = [a.get('a') for a in alist if a.get('a')]
Based on gnibbler's suggestion:
new_list = [a.get('a') for a in alist if 'a' in a ]
I think you need a ternary expression here;
[dic['a'] if 'a' in dic else 'No Data' for dic in alist]
or use dict.get:
[dic.get('a','No Data') for dic in alist]
Here is a way without a list comprehension for the functional programming fans
>>> alist=[{'a':'1a', 'b':'1b'},{'a':'2a','b':'2b'}, {'a':'3a','b':'3b'}]
>>> from operator import itemgetter
>>> list(map(itemgetter('a'), alist))
['1a', '2a', '3a']
To get the "No Data", it's much easier to use the list comprehension
>>> [item.get('a', 'No Data') for item in alist]
['1a', '2a', '3a']
This works because dict.get lets you specify a default argument in case the key is not found

Categories