-1

So I created this code with the help of Stack Overflow users.

def get_name(string):
    return string.replace("+", "").replace("-", "")

def gnames(input_list: list):
    output = {}
    for entry in input_list:
        if '->' in entry:
            names = entry.split('->')
            output[names[1]] = output[names[0]]
            output[names[0]] = 0
        else:
            name = get_name(entry)
            if name not in output:
                output[name] = 0
            if "++" in entry:
                output[name] += 1
            if "--" in entry:
                output[name] -= 1
    return output
print(gnames(["Jim--", "John--", "Jordan--", "Jim++", "John--", "Jeff--", "June++", "June->Jim"]))

and this returns

{'Jim': 1, 'John': -2, 'Jordan': -1, 'Jeff': -1, 'June': 0}

Now this is right, but I want gnames() to return only the non zero values negative numbers or positive numbers are fine

so in my example, there's 'June' = 0

and I want the output of gnames() to exclude 'June' = 0 or if any other person has a 0... I want gnames() to exclude it...

so my output in thiscase, should return

{'Jim': 1, 'John': -2, 'Jordan': -1, 'Jeff': -1}

How can I do that??

bad_coder
  • 11,289
  • 20
  • 44
  • 72

3 Answers3

3

A dictionary comprehension makes this pretty simple. If we start out with gnames being {'Jim': 1, 'John': -2, 'Jordan': -1, 'Jeff': -1, 'June': 0} we can write a dictionary comprehension that will filter out the zero value.

>>> gnames = {'Jim': 1, 'John': -2, 'Jordan': -1, 'Jeff': -1, 'June': 0}
>>> {k: v for k, v in gnames.items() if v != 0}
{'Jim': 1, 'John': -2, 'Jordan': -1, 'Jeff': -1}
>>>
Chris
  • 26,361
  • 5
  • 21
  • 42
1

Also using dictionary comprehension, you can alter your return like so:

return {x:output[x] for x in output if output[x] != 0}

Description of what this does:

# for each element in output
#    if element['name'] != 0 then keep it

Full code:

def get_name(string):
    return string.replace("+", "").replace("-", "")

def gnames(input_list: list):
    output = {}
    for entry in input_list:
        if '->' in entry:
            names = entry.split('->')
            output[names[1]] = output[names[0]]
            output[names[0]] = 0
        else:
            name = get_name(entry)
            if name not in output:
                output[name] = 0
            if "++" in entry:
                output[name] += 1
            if "--" in entry:
                output[name] -= 1
    return {x:output[x] for x in output if output[x] != 0}


print(gnames(["Jim--", "John--", "Jordan--", "Jim++", "John--", "Jeff--", "June++", "June->Jim"]))

# Sample output
# {'Jim': 1, 'John': -2, 'Jordan': -1, 'Jeff': -1}
JacksonB
  • 349
  • 1
  • 5
  • `get_name` can be cleaned up using regular expressions. `def get_name(string): return re.sub(r"[+-]", "", string)` – Chris Nov 23 '21 at 05:40
  • You may also wish to have `gnames` take a function as an argument allowing the criteria to be specified as a lambda, so the check for `0` isn't hard-coded. – Chris Nov 23 '21 at 05:41
0

If you want to avoid an explicit in check to see if the key exists in the dictionary first else set a default value of 0, you can use a collections.defaultdict which is a dict sub-type which automatically sets a default value if the key is not in dict.

So your code could be simplified with defaultdict(int), then with a sign to delta (change) mapping for a slight improvement, then finally you can opt to delete key from the dict if the result of addition is a 0 value; this can be used to avoid the need of a dict comprehension in the return statement altogether.

So now with all the changes added, the end-to-end code looks like below:

from collections import defaultdict


def get_name(string):
    return string.replace("+", "").replace("-", "")


def gnames(input_list: list):
    output = defaultdict(int)
    sign_to_delta = {'++': 1,
                     '--': -1}

    for entry in input_list:
        if '->' in entry:
            from_, to = entry.split('->')
            # Use `pop` to retrieve and also delete the key from dict
            # instead of assigning the value as 0, which is already the
            # default behavior if the key is not present.
            output[to] = output.pop(from_)
        else:
            name = get_name(entry)
            for sign in sign_to_delta:
                if sign in entry:
                    result = output[name] + sign_to_delta[sign]
                    if result:
                        output[name] = result
                    else:
                        del output[name]

    return output


print(gnames(["Jim--", "John--", "Jordan--", "Jim++", "John--", "Jeff--", "June++", "June->Jim"]))
print(gnames(["Jean++", "Jean--", "Jean--", "Jean++"]))

Out:

defaultdict(<class 'int'>, {'John': -2, 'Jordan': -1, 'Jeff': -1, 'Jim': 1})
defaultdict(<class 'int'>, {})
rv.kvetch
  • 9,940
  • 3
  • 24
  • 53
  • I'm just learning dictionary comprehension and finding it quite confusing, but does this solution _really_ simplify the code? I mean, if you were to look at this with fresh eyes, how long would it take to understand what's going on compared to what OP originally had... – JacksonB Nov 23 '21 at 06:02
  • @JacksonB A fair point; but you will note I actually simplified a bit of the OPs logic from what I understood. For example, the `output[names[1]] = output[names[0]]` syntax was quite confusing, so I replaced it with variables like `to` and `from` to better illustrate it. But I also took quite a bit of time to actually understand what the OP was trying to do. In this regards, I don't believe that this adds undue complexity as compared to what was already present in the original code above. – rv.kvetch Nov 23 '21 at 06:05