7

I have the below code where I am trying to append a 1 to the hash of an element, on every occurence of it in input.

def test(Ar):
    hash_table = {}
    for elem in Ar:
        if elem not in hash_table:
            hash_table.setdefault(elem,[]).append(1)
        else:
            hash_table[elem] = hash_table[elem].append(1)
    print(hash_table)

Ar = (1,2,3,4,5,1,2)
test(Ar)

Output:

{1: None, 2: None, 3: [1], 4: [1], 5: [1]}

Expected Output:

{1: [1,1], 2: [1,1], 3: [1], 4: [1], 5: [1]}

I am puzzled why None gets in on doing an append. Kindly explain what is happening.

Note:

On typing the else portion,

hash_table[elem] = hash_table[elem].append(1) # the append() was not suggested at all by the IDE. I forcibly put it, hoping things will work.
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
KurinchiMalar
  • 598
  • 3
  • 12
  • 28

2 Answers2

11

The list.append is an in-place operation. So it just modifies the list object and doesn't return anything. That is why, by default, None is getting returned from list.append and you are storing that corresponding to the key in this line

hash_table[elem] = hash_table[elem].append(1)

In your case, you don't need the if condition at all.

def test(Ar):
    hash_table = {}
    for elem in Ar:
        hash_table.setdefault(elem, []).append(1)
    print(hash_table)

because, setdefault will first look for the key elem in it and it found something, then it will return the value corresponding to it. And if it doesn't then it will create key elem and use the second argument passed to it as the value and then return then value.


Instead of using this, you can use collections.defaultdict, like this

from collections import defaultdict
def test(Ar):
    hash_table = defaultdict(list)
    for elem in Ar:
        hash_table[elem].append(1)
    print(hash_table)

This would do almost the same thing as the setdefault version


It looks like you are trying to find the frequency of elements. In that case you can simply use collections.Counter

from collections import Counter
Ar = (1, 2, 3, 4, 5, 1, 2)
print Counter(Ar)
# Counter({1: 2, 2: 2, 3: 1, 4: 1, 5: 1})

This will give the number of times each and every element occurred in the iterable passed.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • Also note that `defaultdict` have a `default_factory` property, which when set to `None` makes the default dict behave like a regular dict once again. This is particularly useful for functions which want to construct a dictionary using defaultdict behaviors, but then want to return something that behaves like a regular dict. – mgilson Jan 15 '16 at 16:35
1

The bug is in this line:

hash_table[elem] = hash_table[elem].append(1)

list.append(x) returns None, which you are assigning to hash_table[elem]

The else part is unnecessary, but still for getting correct results, remove the assignment in the else part.


Note that the None is only for elements that occur twice. If any element existed thrice, you'd have gotten an error.

jatinderjit
  • 1,359
  • 8
  • 21