Patrick Vaillancourt
Patrick Vaillancourt

Reputation: 51

Adding more than one value to dictionary when looping through string

Still super new to Python 3 and have encountered a problem... I am trying to create a function which returns a dictionary with the keys being the length of each word and the values being the words in the string.

For example, if my string is: "The dogs run quickly forward to the park", my dictionary should return {2: ['to'] 3: ['The', 'run', 'the'], 4: ['dogs', 'park], 7: ['quickly', 'forward']}

Problem is that when I loop through the items, it is only appending one of the words in the string.

def word_len_dict(my_string):
    dictionary = {}
    input_list = my_string.split(" ")
    unique_list = []
    for item in input_list:
        if item.lower() not in unique_list:
            unique_list.append(item.lower())
    for word in unique_list:
        dictionary[len(word)] = []
        dictionary[len(word)].append(word)
    return (dictionary)

print (word_len_dict("The dogs run quickly forward to the park"))

The code returns

{2: ['to'], 3: ['run'], 4: ['park'], 7: ['forward']}

Can someone point me in the right direction? Perhaps not giving me the answer freely, but what do I need to look at next in terms of adding the missing words to the list. I thought that appending them to the list would do it, but it's not.

Thank you!

Upvotes: 0

Views: 5700

Answers (7)

modpy
modpy

Reputation: 711

This option starts by creating a unique set of lowercase words and then takes advantage of dict's setdefault to avoid searching the dictionary keys multiple times.

>>> a = "The dogs run quickly forward to the park"
>>> b = set((word.lower() for word in a.split()))
>>> result = {}
>>> {result.setdefault(len(word), []).append(word.lower()) for word in b}
{None}
>>> result
{2: ['to'], 3: ['the', 'run'], 4: ['park', 'dogs'], 7: ['quickly', 'forward']}

Upvotes: 0

Hackaholic
Hackaholic

Reputation: 19771

Pythonic way,

Using itertools.groupby

>>> my_str = "The dogs run quickly forward to the park"
>>> {x:list(y) for x,y in itertools.groupby(sorted(my_str.split(),key=len), key=lambda x:len(x))}
{2: ['to'], 3: ['The', 'run', 'the'], 4: ['dogs', 'park'], 7: ['quickly', 'forward']}

Upvotes: 1

Burhan Khalid
Burhan Khalid

Reputation: 174748

Your code is simply resetting the key to an empty list each time, which is why you only get one value (the last value) in the list for each key.

To make sure there are no duplicates, you can set the default value of a key to a set which is a collection that enforces uniqueness (in other words, there can be no duplicates in a set).

def word_len_dict(my_string):
    dictionary = {}
    input_list = my_string.split(" ")
    for word in input_list:
        if len(word) not in dictionary:
            dictionary[len(word)] = set()
        dictionary[len(word)].add(word.lower())
    return dictionary

Once you add that check, you can get rid of the first loop as well. Now it will work as expected.

You can also optimize the code further, by using the setdefault method of dictionaries.

for word in input_list:
   dictionary.setdefault(len(word), set()).add(word.lower())

Upvotes: 1

Mehrdad
Mehrdad

Reputation: 2116

for word in unique_list:
dictionary[len(word)] = [x for x in input_list if len(x) == len(word)]

Upvotes: 1

Cezar Spatariu Neagu
Cezar Spatariu Neagu

Reputation: 48

you can create first the list of the unique words like this in order to avoid a first loop, and populate the dictionary on a second step.

unique_string = set("The dogs run quickly forward to the park".lower().split(" "))
dict = {}


for word in unique_string:
    key, value = len(word), word
    if key not in dict:         # or dict.keys() for better readability (but is the same)
        dict[key] = [value]
    else:
        dict[key].append(value)

print(dict)

Upvotes: 2

Tom Karzes
Tom Karzes

Reputation: 24100

This will solve all your problems:

def word_len_dict(my_string):
    input_list = my_string.split(" ")

    unique_set = set()
    dictionary = {}

    for item in input_list:
        word = item.lower()
        if word not in unique_set:
            unique_set.add(word)
            key = len(word)
            if key not in dictionary:
                dictionary[key] = []
            dictionary[key].append(word)

    return dictionary

You were wiping dict entries each time you encountered a new word. There were also some efficiencly problems (searching a list for membership while growing it resulted in an O(n**2) algorithm for an O(n) task). Replacing the list membership test with a set membership test corrected the efficiency problem.

It gives the correct output for your sample sentence:

>>> print(word_len_dict("The dogs run quickly forward to the park"))
{2: ['to'], 3: ['the', 'run'], 4: ['dogs', 'park'], 7: ['quickly', 'forward']}

I noticed some of the other posted solutions are failing to map words to lowercase and/or failing to remove duplicates, which you clearly wanted.

Upvotes: 2

Matt Jordan
Matt Jordan

Reputation: 2181

You are assigning an empty list to the dictionary item before you append the latest word, which erases all previous words.

Upvotes: 1

Related Questions