3

I have a nested dictionary and I would like to replace all the strings in the lists that have a space followed by numbers (vlc 2.2, ado 3.4 and ultr 3.1) just with their name, i.e. vlc, ado and ultr. Here is the input dictionary:

input = {'cl1': {'to_do': ['ab',
'dir8',
'cop',
'vlc 2.2.2.0',
'7zi',
'7zi',
'ado 3.4']},
'cl2': {'to_do': ['ultr 3.1', 'ab']}}

This should be the output:

result = {'cl1': {'to_do': ['ab',
'dir8',
'cop',
'vlc',
'7zi',
'7zi',
'ado']},
'cl2': {'to_do': ['ultr', 'ab']}}

I am trying something like:

for k in input:
    for e in input[k]['to_do']:
        input[k]['to_do'] = e.replace(e, e.split()[0])

Getting the wrong output:

{'cl1': {'to_do': 'ado'}, 'cl2': {'to_do': 'ab'}}

I don't fully understand where is the mistake. Any help? Thank you

2
  • Will there be strings with spaces that are not followed by numbers, e.g. ab cd, and do you want to keep the latter part in this case? Commented Aug 16, 2016 at 8:03
  • @tobias_k The strings with a space will be only followed by numbers Commented Aug 16, 2016 at 8:04

3 Answers 3

5

An even more generic way to do this would be to create a recursive method, that calls itself again:

def recursive_split(input, search):
    # check whether it's a dict, list, tuple, or scalar
    if isinstance(input, dict):
        items = input.items()
    elif isinstance(input, (list, tuple)):
        items = enumerate(input)
    else:
        # just a value, split and return
        return str(input).split(search)[0]

    # now call ourself for every value and replace in the input
    for key, value in items:
        input[key] = recursive_split(value, search)
    return input

Please note, that this approach uses in-place replacement, but could easily be converted to return a new dictionary instead. This should cover any structure containing any type of values, that can be transformed into strings. In your case you would use it as by simply calling:

d = recursive_split(d, " ")
Sign up to request clarification or add additional context in comments.

2 Comments

my brain cannot proceed with the way to return a new dictionary instead. seems hard. since you cannot dynamically add square bracket into dictionary variable e.g dict[x][y][z][.. and so on] to change its variable
@lone_coder You just have to replace the input with a new dict. Use the isinstance checks before to determine whether you need a list or a dict, create a new one and then set the results on this new object instead of the original one in the for loop. Note, that for a list, you might need to use the append method instead.
2

When you do this :

input[k]['to_do'] = e.replace(e, e.split()[0])

You replace the initial arrays (['ultr 3.1', 'ab'] and ['ab', 'dir8', 'cop', 'vlc 2.2.2.0', '7zi', '7zi', 'ado 3.4']) by a single value (the last one processed in your loop).

You just have to replace the inner loop by this single line to make it work :

input[k]['to_do'] = [e.split()[0] for e in input[k]['to_do']]

The correct result is given :

 {'cl2': {'to_do': ['ultr', 'ab']},
  'cl1': {'to_do': ['ab', 'dir8', 'cop', 'vlc', '7zi', '7zi', 'ado']}}  

1 Comment

"You just have to replace the line above" In fact, you should replace the entire inner loop with that line, not just the assignment within the loop.
0

This solution will work if you want to strip anything following a space within the to_do lists. This seemed implied by your current implementation, but if you want digits only, you'll need to use a regex.

Python 2:

d = {
    'cl1': {
        'to_do': [
            'ab',
            'dir8',
            'cop',
            'vlc 2.2.2.0',
            '7zi',
            '7zi',
            'ado 3.4'
        ]
    },
    'cl2': {
        'to_do': [
            'ultr 3.1',
            'ab'
        ]
    }
}

for inner_dict in d.itervalues():
    inner_dict['to_do'] = [x.split()[0] for x in inner_dict['to_do']]

print d  # {'cl1': {'to_do': ['ab', 'dir8', 'cop', 'vlc', '7zi', '7zi', 'ado']}, 'cl2': {'to_do': ['ultr', 'ab']}}

Python 3 (assume the same d):

for inner_dict in d.values():
    inner_dict['to_do'] = [x.split()[0] for x in inner_dict['to_do']]

print(d)

1 Comment

its appear to be a key error. since you directly call the "to_do" key without its parent key "cl1"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.