-3

I'm new to python and struggling with this question - Create a function which works as follows:

d={1:"x",2:"y",3:"x",4:"z"}

invert_d(d)
{"x":(1,3),"y":(2),"z":(4)}

So invert_d gives a new dict with the values of multiple keys as lists.

I have to say, that I never worked with dicts and started python 1 week ago...so I'm a total newb.

I am new to python.... and I'm struggling with this question:

Edit: I read wrong and fixed the Dict. Sorry guys :(

Also we cant import in the exam

miton
  • 1
  • 2

4 Answers4

1

You can use the setdefault method to make the dictionary construction simpler. A very basic way to do it is like this:

d={1:"x",2:"y",3:"x",4:"z"}

def invertDict(d):
    result = dict()
    for k,v in d.items(): result.setdefault(v,[]).append(k)
    return result

print(invertDict(d))

{'x': [1, 3], 'y': [2], 'z': [4]}

If you want a one-liner solution, you can use a dictionary comprehension. Here's one that outputs the list of keys as tuples:

def invertDict(d):
    return {v:tuple(inv[v]) for inv in [{}] for k,v in d.items() if [inv.setdefault(v,[]).append(k)] }

print(invertDict(d))

{'x': (1, 3), 'y': (2,), 'z': (4,)}
Alain T.
  • 40,517
  • 4
  • 31
  • 51
0

A dict associates exactly one value with a particular key, so your original dict d would see two different values for the key "x" (first 1 and then 3), and the last one would replace previous ones. If you have access to an interactive Python session, this looks like:

$ python
Python 3.9.1 (default, Dec 13 2020, 11:55:53) 
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> d = {"x": 1, "y": 2, "x": 3, "z": 5}
>>> d
{'x': 3, 'y': 2, 'z': 5}

(The d by itself there just asks Python to use print to display the current value, as if you had called print(d).)

So at this point you have some options. You could just define a dict to contain the eventual values you want: {"x":(1,3),"y":(2,),"z":(5,)}. Or, if you actually have a source of a stream or list of pairs of values that you want to process into a dict using a function like the one you describe, we can do that, too, but we need to use something like a list of tuples as the input, rather than a dict: l = [("x", 1), ("y", 2), ("x", 3), ("z", 5)]. With that new notion of the input, we could write a function that in some way processes each element of that list and compiles those elements into a particular dict. Here's one way that could look (without any error handling):

from functools import reduce

def add_tuple_to_dict(d, t): # here `d` is the dict and `t` is the tuple
  if t[0] in d:
    d[t[0]].append(t[1])
  else:
    d[t[0]] = [t[1],]
  return d

def invert_d(list_of_tuples):
  return reduce(add_tuple_to_dict, list_of_tuples, {})

Edit: after submitting the above, I now realize you do actually want to invert your dict, which could look like this, instead:

from functools import reduce

def add_tuple_to_dict(d, t): # here `d` is the dict and `t` is the tuple
  if t[1] in d:
    d[t[1]].append(t[0])
  else:
    d[t[1]] = [t[0],]
  return d

def invert_d(d):
  return reduce(add_tuple_to_dict, d.items(), {})
  • thanks, in the exam we cant import new things and have to work with basics python gives – miton Feb 01 '21 at 17:44
0

You can use collections.defaultdict to initially create your dictionary having keys with lists as values. Then convert it to a dict having keys with tuples as values:

import collections

d = {1: "x", 2: "y", 3: "x", 4: "z"}

def invert(d):
    # first as a dict with lists as values
    inv = collections.defaultdict(list)
    for k, v in d.items():
        # note that the previous VALUE is the key of the inverted dict
        inv[v].append(k)
    
    # then convert the existing "list of values" to "tuples of values"
    inv = {k: tuple(v) for k, v in inv.items()}
    
    return inv

invert(d)
# {'x': (1, 3), 'y': (2,), 'z': (4,)}

What defaultdict does is when a value in the dict is looked up (get or set), if it doesn't exist, it automatically creates a value with the default factory provided. In this case, list.
So each value when looked up is set to an empty list. In the loop, the new values (keys of the old one), are appended to this list. If it didn't exist before, the new value is appended to the empty list. If it did exist (from a previous iteration) and has a value, then a new value is appended to that list.

Since what you want at the end is tuples of values and not lists, I have a dictionary comprehension which uses the same keys and converts the values to tuples.

aneroid
  • 12,983
  • 3
  • 36
  • 66
  • thanks, in the exam we cant import new things and have to work with basics python gives – miton Feb 01 '21 at 17:43
  • `collections` is not an external package, it's part of the "basics" Python comes with. If you don't want to or can't import external packages, then mention that clearly in your question. – aneroid Feb 01 '21 at 17:45
  • Sorry, i fixed it – miton Feb 01 '21 at 17:47
  • You can add a version where the logic I've explained is coded in explicitly. _How it should work_ is in the explanation of my answer. Try writing it yourself first. – aneroid Feb 01 '21 at 17:47
0

You can do a reverse look-up

invert_d = lambda d: { v:[k for k in d if d[k] == v] for v in d.values() }

whereat you can read v as value and k as key.

d={1:"x",2:"y",3:"x",4:"z"}
invert_d(d)

{'x': [1, 3], 'y': [2], 'z': [4]}

Edit

Maybe a brief explanation: This is a Lambda Expressions using two List Comprehensions. One as value and an outer one to wrap it up (actually a dictionary comprehensions but works the same). This is not really performant but totally sufficient for every day usage.

Suuuehgi
  • 4,547
  • 3
  • 27
  • 32