Get first and second values in dictionary in CPython 3.6 - python

Now that dictionaries are ordered in python 3.6, it must be the case that there is a way to get the first and second values of a dictionary in only two lines. Right now, I have to use 7 lines to accomplish this:
for key, value in class_sent.items():
i += 1
if i == 1:
first_sent = value
elif i == 2:
second_sent = value
I also tried:
first_sent = next(iter(class_sent))
second_sent = next(iter(class_sent))
But in that case the second_sent is equal to the first_sent. If anyone knows how to obtain the first and second values in a dictionary in as few lines as possible I would seriously appreciate it.

Right now Python only guarantees that the order of **kwargs and class attributes are preserved.
Considering that the implementation of Python you're using guarantees this behaviour you could do.
Using itertools.islice.
>>> from itertools import islice
>>> dct = {'a': 1, 'b': 2, 'c': 3}
>>> first, second = islice(dct.values(), 2)
>>> first, second
(1, 2)
Using iter().
>>> it = iter(dct.values())
>>> first, second = next(it), next(it)
>>> first, second
(1, 2)
Using extended iterable unpacking(will result in unnecessary unpacking of other values as well):
>>> first, second, *_ = dct.values()
>>> first, second
(1, 2)

This could work:
first_sent, second_sent = list(class_sent.values())[:2]

Related

Python defaultdict

I found something strange that I couldn't understand.
This is the case:
from collections import defaultdict
a = defaultdict(lambda: len(a))
This is just the part of the code, and the code has never defined 'a' above.
The questions are:
Is it possible to use defaultdict as is, not specifying the variable previously?
If possible, what is the meaning of that code?
Maybe it is best explained in an example:
>>> a = defaultdict(lambda: len(b))
>>> b = 'abcd'
>>> a[0]
4
As you can see, it is possible to use b in the lambda even though the b does not yet exist at that point. What is important is that b exists at the time when the lambda is executed. At that point, Python will look for a variable named b and use it.
Note also that the original code does not necessarily use length of the defaultdict itself. It simply evaluates whatever a is at that point. See this example:
>>> a = defaultdict(lambda: len(a))
>>> a['a']
0
>>> a['b']
1
So far, so good. But then rename some things:
>>> x = a
>>> a = []
>>> x['c']
0
>>> x['d']
0
Now the deaultdict is named x, but it does not use len(x). It still uses len(a). This caveat may become important if you sent the defaultdict to a function where a does not mean anything.
you are saying to default dict, when i try to do something with a key and it doesnt exist, use this lambda as the inital value for the key. since your lambda is using a (i.E the dict its self) and you say the length of it. It means when you perform operations using a key thats not in the dict then the dict will use the lambda instead or in this case the length of the dict as the value
from collections import defaultdict
a = defaultdict(lambda: len(a))
a['one'] += 5 #here dict length is 0 so value is 0 + 5 = 5
a['two'] += 2 #jere dict length is 1 so value is 1 + 2 = 3
a['three'] += 1 #here dict length is 2 so value is 2 + 1 = 3
print(a.items())
print(a['newval']) #here newval doesnt exist so will use default value which is length of dict I.E 3
OUTPUT
dict_items([('one', 5), ('two', 3), ('three', 3)])
3
Here's how defaultdict works. Say you have a dict of lists and you're setting values for keys that might not exist. In that case you'd do something like this:
d = dict()
if some_key not in d:
d[some_key] = list()
d[some_key].append(some_value)
defaultdict does this automatically for you by passing it a callable, e.g., int, list, set, which will call int() (default value 0), list() (default value empty list), and set() (default value empty set) respectively. Your lambda is also a callable, which returns integers, so you'll have a dict with int values. But the value you get from the expression will depend on the size of the dict.
Can you do a = defaultdict(lambda: len(a))?
Yes, you can. The lambda will not be executed until called which is when it'll look up the name a. Compare these two cases.
f = lambda: len(a)
a = defaultdict(f)
a[0] # this is when the lambda is called for the first time
But,
g = lambda: len(b)
g() # this will raise a NameError
b = defauldict(g)

Fill list using Counter with 0 values

Is possible to have a count of how many times a value appears in a given list, and have '0' if the item is not in the list?
I have to use zip but the first list have 5 items and the other one created using count, have only 3. That's why I need to fill the other two position with 0 values.
You can achieve your purpose with itertools zip_longest.
With zip_longest, you can zip two lists of different lengths, just that the missing corresponding values will be filled with 'None'. You may define a suitable fill values as i have done below.
from itertools import zip_longest
a = ['a','b','c','d','e']
b = [1,4,3]
final_lst = list(zip_longest(a,b, fillvalue=0))
final_dict = dict(list(zip_longest(a,b, fillvalue=0))) #you may convert answer to dictionary if you wish
ELSE
If what you are trying to do is count the number of times items in a reference list appear in another list(taking record also of reference items that don't appear in the other list), you may use dictionary comprehension:
ref_list = ['a','b','c','d','e']#reference list
other_list = ['a','b','b','d','a','d','a','a','a']
count_dict = {n:other_list.count(n) for n in ref_list}
print (count_dict)
Output
{'a': 5, 'b': 2, 'c': 0, 'd': 2, 'e': 0}
Use collections.Counter, and then call get with a default value of 0 to see how many times any given element appears:
>>> from collections import Counter
>>> counts = Counter([1, 2, 3, 1])
>>> counts.get(1, 0)
2
>>> counts.get(2, 0)
1
>>> counts.get(5, 0)
0
If you want to count how many times a value appears in a list, you could do this:
def count_in_list(list_,value):
count=0
for e in list_:
if e==value:
count+=1
return count
And use the code like this:
MyList=[1,3,1,1,1,1,1,2]
count_in_list(MyList,1)
Output:
6
This will work without any additional things such as imports.

Python generator function to loop over iterable sequence while eliminating duplicates

I am trying to create a generator function that loops over an iterable sequence while eliminating duplicates and then returns each result in order one at a time (not as a set or list), but I am having difficulty getting it to work. I have found similar questions here, but the responses pretty uniformly result in a list being produced.
I would like the output to be something like:
>>> next(i)
2
>>> next(i)
8
>>> next(i)
4....
I was able to write it as a regular function that produces a list:
def unique(series):
new_series = []
for i in series:
if i not in new_series:
new_series.append(i)
return new_series
series = ([2,8,4,5,5,6,6,6,2,1])
print(unique(series))
I then tried rewriting it as a generator function by eliminating the lines that create a blank list and that append to that list, and then using "yield" instead of "return"; but I’m not getting it to work:
def unique(series):
for i in series:
if i not in new_series:
yield new_series
I don't know if I'm leaving something out or putting too much in. Thank you for any assistance.
Well, to put it simply, you need something to "remember" the values you find. In your first function you were using the new list itself, but in the second one you don't have it, so it fails. You can use a set() for this purpose.
def unique(series):
seen = set()
for i in series:
if i not in seen:
seen.add(i)
yield i
Also, yield should "yield" a single value at once, not the entire new list.
To print out the elements, you'll have to iterate on the generator. Simply doing print(unique([1, 2, 3])) will print the resulting generator object.
>>> print(unique([1, 1, 2, 3]))
<generator object unique at 0x1023bda98>
>>> print(*unique([1, 1, 2, 3]))
1 2 3
>>> for x in unique([1, 1, 2, 3]):
print(x)
1
2
3
Note: * in the second example is the iterable unpack operator.
Try this:
def unique(series):
new_se = []
for i in series:
if i not in new_se:
new_se.append(i)
new_se = list(dict.fromkeys(new_se)) # this will remove duplicates
return new_se
series = [2,8,4,5,5,6,6,6,2,1]
print(unique(series))

how to ignore the order of elements in a tuple

I am using tuples as the key for a dictionary I created. For example:
example_dict = {}
example_dict[("A", "B")] = "1"
Later when I wish to modify the value of an entry in the dictionary I don't currently have control over the order of the tuple. For example:
("B", "A") may be the case, instead of ("A", "B")
I'm aware that these tuples are not equal from a simple == comparison that I tried in the python shell.
What I am wondering is how I could work around this? How could I make the following not produce a KeyError:
print (example_dict["B", "A"])
Is there a way to consistently order the elements of a tuple? Is there a way to ignore order completely? Any other work arounds? I'm aware I could just include all arrangements of the tuples as keys in the dictionary, and then collate the values of the different permutations later. I strongly want to avoid doing this as that only adds difficulty and complexity to the problem.
The usual ways are to either sort the keys:
example_dict[tuple(sorted(key_tuple))] = "1"
use frozensets as keys (if there won't be duplicate elements in the tuples):
example_dict[frozenset(key_tuple)] = "1"
or use frozensets of (item, count) tuples as keys (if there can be duplicate elements in the tuples):
example_dict[frozenset(Counter(key_tuple).viewitems())] = "1"
Whichever option you choose, you'll have to apply the same transformation when you look up values.
You want your dictionary keys to be "sets" (a set is a collection for which an item is either in or not in the set, but that has no concept of order). Luckily python has what you need. Specifically because you need something hashable you want to use frozenset.
>>> example_dict = {}
>>> example_dict[frozenset(("A", "B"))] = "1"
>>> example_dict[frozenset(("B", "A"))]
'1'
>>> example_dict[frozenset(("A", "B"))]
'1'
Instead of using a tuple, use a frozenset. A frozenset is just a constant set, just as a tuple can be thought of as a constant list.
Here's an example (from Python 3, but it will work in Python 2 as well):
>>> d = {}
>>> k1 = frozenset((1, 2))
>>> k2 = frozenset((2, 1))
>>> k1
frozenset({1, 2})
>>> k2
frozenset({1, 2})
>>> k1 == k2
True
>>> d[k1] = 123
>>> d[k2]
123
>>>

Python 2.7 Counting number of dictionary items with given value

first question here, so i will get right to it:
using python 2.7
I have a dictionary of items, the keys are an x,y coordinate represented as a tuple: (x,y) and all the values are Boolean values.
I am trying to figure out a quick and clean method of getting a count of how many items have a given value. I do NOT need to know which keys have the given value, just how many.
there is a similar post here:
How many items in a dictionary share the same value in Python, however I do not need a dictionary returned, just an integer.
My first thought is to iterate over the items and test each one while keeping a count of each True value or something. I am just wondering, since I am still new to python and don't know all the libraries, if there is a better/faster/simpler way to do this.
thanks in advance.
This first part is mostly for fun -- I probably wouldn't use it in my code.
sum(d.values())
will get the number of True values. (Of course, you can get the number of False values by len(d) - sum(d.values())).
Slightly more generally, you can do something like:
sum(1 for x in d.values() if some_condition(x))
In this case, if x works just fine in place of if some_condition(x) and is what most people would use in real-world code)
OF THE THREE SOLUTIONS I HAVE POSTED HERE, THE ABOVE IS THE MOST IDIOMATIC AND IS THE ONE I WOULD RECOMMEND
Finally, I suppose this could be written a little more cleverly:
sum( x == chosen_value for x in d.values() )
This is in the same vein as my first (fun) solution as it relies on the fact that True + True == 2. Clever isn't always better. I think most people would consider this version to be a little more obscure than the one above (and therefore worse).
If you want a data structure that you can quickly access to check the counts, you could try using a Counter (as #mgilson points out, this relies on the values themselves being hashable):
>>> from collections import Counter
>>> d = {(1, 2): 2, (3, 1): 2, (4, 4): 1, (5, 6): 4}
>>> Counter(d.values())
Counter({2: 2, 1: 1, 4: 1})
You could then plug in a value and get the number of times it appeared:
>>> c = Counter(d.values())
>>> c[2]
2
>>> c[4]
1

Categories

Resources