Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ternary expression in dictionary comprehension

I'm trying to invert a dictionary. In the case of many keys having the same value, the new key (old value) should associate with a set of the new values (old keys). I solved the problem, but I'm trying to refactor using dictionary comprehension, and some helper methods.

def has_unique_value(k):
    return d.values().count(d[k]) == 1

def keys_with_same_value_as_key(k):
    return set([key for key in d.keys() if d[key] == d[k]])

print( {d[k]:k if has_unique_value(k) else d[k]:keys_with_same_value_as_key(k) for k in d.keys()} )

However, this raises a syntax error

print( {d[k]:k if has_unique_value(k) else d[k]:keys_with_same_value_as_key(k) for k in d} )
                                               ^
SyntaxError: invalid syntax

Hopefully the alignment in that code works right. It should point to the second :, in the else clause. Any idea what's up here? I've tried as many forms of parenthesizing as I can conceive.

like image 379
Brad Rice Avatar asked Jan 18 '14 20:01

Brad Rice


People also ask

How dictionary comprehension works?

All items in the dictionary are enclosed within a pair of curly braces {} . Each item in a dictionary is a mapping between a key and a value - called a key-value pair. A key-value pair is often called a dictionary item. You can access these values using the respective keys.

What is ternary syntax in Python?

Practical Data Science using Python Similarly the ternary operator in python is used to return a value based on the result of a binary condition. It takes binary value(condition) as an input, so it looks similar to an “if-else” condition block. However, it also returns a value so behaving similar to a function.

How do you write if-else in comprehension?

The final if clause acts as a filter, which is different from having the conditional expression. Worth mentioning that you don't need to have an if-else condition for both the key and the value. For example, {(a if condition else b): value for key, value in dict. items()} will work.


2 Answers

Close!

The following code

d = {'a': 'x', 'b': 'y', 'c': 'x'}

def has_unique_value(k):
    return d.values().count(d[k]) == 1

def keys_with_same_value_as_key(k):
    return set([key for key in d.keys() if d[key] == d[k]])

print( {d[k]:k if has_unique_value(k) else keys_with_same_value_as_key(k) for k in d.keys()} )

Produces

{'y': 'b', 'x': set(['a', 'c'])}

The only difference is the second d[k]: is removed.


In general, the ternary expression looks like

a = val_if_true if test else val_if_false

not something closer to what you had:

a = val_if_true if test else a = val_if_false

You specify where the value from the ternary expression should go once, at the beginning.


RE: Question in comments

This is still definitely a dictionary comprehension.

It's basically doing the following:

m = {}
for k in d.keys():
    orig_key = k
    orig_val = d[k]

    if has_unique_value(k):
        m[orig_val] = orig_key
    else:
        m[orig_val] = keys_with_same_value_as_key(orig_key)

print m

The only difference is that, in the dictionary comprehension, the m dictionary is not kept in the namespace (and the variables orig_key and orig_val I used to clarify the code never exist).

like image 90
jedwards Avatar answered Sep 22 '22 05:09

jedwards


The ternary expression can only be applied to one value and not to the equivalent of a dictionary assignment. Try this instead:

{d[k]: k if has_unique_value(k) else keys_with_same_value_as_key(k) for k in d.keys()}

Your first approach would be similar to the following (which does not work):

d[k] = k if k else d[k] = None  # doesn't work
like image 28
Justin O Barber Avatar answered Sep 22 '22 05:09

Justin O Barber