4

I am looking for a short and compact one-liner to initialize a dictionary from a list in Python. Is there an equivalent to the two constructs below?

dic = {}
for elt in list:
  dic[elt.hash] = elt

.

dic2 = {}
defaultValue = 0
for elt in list:
  dic2[elt] = defaultValue

I've see the use of Counter for the second case, but it is a very narrow case, and I'm looking for a generic syntax.

Cœur
  • 37,241
  • 25
  • 195
  • 267
PPC
  • 1,734
  • 1
  • 21
  • 41
  • 1
    the second example isn't optimally built via dictionary comprehension. `dict.fromkeys` or `collections.defaultdict` is more suitable here. – jpp Feb 04 '18 at 15:49

4 Answers4

7

Summary of three pythonic solutions to instantiating dictionaries. Having a "one-line" solution should never be the most important consideration.

1. Keys and default value defined in advance.

Instead of setting a default value for every key, use dict.fromkeys. Also, do not name variables the same as classes, e.g. use lst instead.

dic2 = dict.fromkeys(lst, 0)

2. Default value defined in advance, but not keys.

Alternatively, if you will be adding more keys in the future, consider using collections.defaultdict, which is a subclass of dict:

from collections import defaultdict
dic2 = defaultdict(int)
dic2['newkey']  # returns 0 even not explicitly set

3. Keys and values related by a function.

For building a dictionary where keys and values are linked via a function, use a dictionary comprehension, as described in detail by @OmerB, e.g.

{k: f(k) for k in lst}  # value function of given keys
{f(v): v for v in lst}  # key function of given values
jpp
  • 159,742
  • 34
  • 281
  • 339
4

Well, dictionary comprehension is one-line, is that what you mean?

>> MyList = ['apple', 'banana', 'pear']
>> {hash(element): element for element in MyList}

{-8723846192743330971: 'pear',
 -6060554681585566095: 'banana',
 -4133088065282473265: 'apple'}

Also - I'm suspecting that using the element's hash as the key is not what you're looking for:

  • If your elements are immutable (e.g. strings), you could use the elements themselves as keys.
  • If they're not immutable, you can use the element's location in the list.

For the latter, to make sure you insert each value only once to the dict, clear duplicates from the list before iterating over it:

>> MyList = ['apple', 'apple', 'banana', 'banana', 'pear']
>> {idx: value for (idx,value) in enumerate(set(MyList))}
{0: 'banana', 1: 'pear', 2: 'apple'}
OmerB
  • 4,134
  • 3
  • 20
  • 33
1

A simple one liner statement for case-01 using dict constructor, map and zip:

>>> l = ['a','b','c']
>>> dict(zip(map(hash,l),l))
>>> {12416037344: 'a', 12672038114: 'c', 12544037731: 'b'}

For second situation:

>>> l = ['a','b','c']
>>> default_value = 10
>>> dict((zip(l,[default_value]*len(l))))
>>> {'a': 10, 'c': 10, 'b': 10}
Sohaib Farooqi
  • 5,457
  • 4
  • 31
  • 43
0

Those two constructs can be built using dictionary comprehensions like:

Code:

dic_comp = {getattr(elt, 'hash'): elt for elt in test_data}
dic2_comp = {elt: defaultValue for elt in test_data}

Test Code:

class TestObj(object):
    def __init__(self, hash_value):
        self.hash = hash_value

test_data = [TestObj(i) for i in range(5)]

dic = {}
for elt in test_data:
    dic[elt.hash] = elt

dic_comp = {getattr(elt, 'hash'): elt for elt in test_data}
assert dic == dic_comp

dic2 = {}
defaultValue = 0
for elt in test_data:
    dic2[elt] = defaultValue

dic2_comp = {elt: defaultValue for elt in test_data}
assert dic2 == dic2_comp
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135