Making my dictionary comprehension more efficient

Viewed 112

I have a list of strings. For example:

lst = ['aa bb cc', 'dd ee ff gg']

Each string in the list is known to contain 2 or more whitespace delimited tokens.

I want to build a dictionary keyed by the last token with the first token as its value.

The following dictionary comprehension achieves this:

d = {e.split()[-1]: e.split()[0] for e in lst}

This gives me:

{'cc': 'aa', 'gg': 'dd'}

...which is exactly what I want.

However, this means that the element e will have its split() function called twice per iteration over lst.

I can't help thinking that there must be a way to avoid this but I just can't figure it out.

Any ideas?

4 Answers

Using map:

d = {v[-1]: v[0] for v in map(str.split, lst)}

You can use walrus operator. But list comprehension is not meant to efficient, it's just shorter(I think)

lst = ['aa bb cc', 'dd ee ff gg']

d={j[-1]:j[0] for i in lst if (j:=i.split())}
print(d)

If you use a function it would be much cleaner as you are carrying out an additional instruction, not just creating a list. Since you are looking to create a single holder of split values you will need to ensure there is some way to access index 0 or -1.

But it can be done like so:

d = {e[0]: e[-1] for e in [item.split() for item in lst]}

Should work for you.

Updated answer for python 3.8 +

I just recalled a recent addition on this. You can also achieve this without the inner comprehension:

d = {parts[0]: parts[-1] for e in lst if (parts := e.split())}

https://www.python.org/dev/peps/pep-0572/

As an alternative to other answers, you can make use of unpacking to get rid of the indexing, IF the elements in your list are always going to have at least 2 strings separated by spaces (which as you say, they do), otherwise this won't work:

d = {last: first for first, *_, last in map(str.split, lst)}
print(d)
# {'cc': 'aa', 'gg': 'dd'}
Related