0

How to write a function that takes a list of strings and a list of characters as arguments and returns a dictionary whose keys are the characters and whose values are lists of the strings that start with that character.

a_func(['apple','orange','banana','berry','corn'],['A','B','C'])

should give me

{'A':['apple'], 'B':['banana','berry'], 'C':['corn]}

I tried this but not sure how to put if condition in this,

a = ['apple','orange','banana','berry','corn']
b = ['A','B','C']
d = {}
for k,v in zip(b,a):
    d.setdefault(k, []).append(v)
    print (d)



---------
#output is
{'A': ['apple']}
{'A': ['apple'], 'B': ['orange']}
{'A': ['apple'], 'C': ['banana'], 'B': ['orange']}
tech.77 r
  • 13
  • 2

3 Answers3

1

This is what happens when you call zip the way you are trying to use it:

>>> list(zip(['A','B','C'], ['apple','orange','banana','berry','corn']))
[('A', 'apple'), ('B', 'orange'), ('C', 'banana')]

Which, as you can see, clearly does not match up to any helpful result you can use to get to your required output.

A much easier way to do this is to make use of defaultdict. Call your defaultdict with a list, so that the initial value of the dictionary entry will be a list. That way all you have to do is check the first character of each word against the character list using in. From there, just simply append once you find that your first character exists in your character list.

Also, you seem to have lower case words with an uppercase character list, so you should set the casing accordingly.


from collections import defaultdict


def a_func(words, chars):
    d = defaultdict(list)
    for word in words:
        upper_char = word[0].upper()
        if upper_char in chars:
            d[upper_char].append(word)
    return d


res = a_func(['apple','orange','banana','berry','corn'],['A','B','C'])

Result:

defaultdict(<class 'list'>, {'A': ['apple'], 'B': ['banana', 'berry'], 'C': ['corn']})
idjaw
  • 25,487
  • 7
  • 64
  • 83
0

What you basically doing is zipping your list into tuple pairs and since zip() stops at the shortest list, you end up with the output:

[('A', 'apple'), ('B', 'orange'), ('C', 'banana')]

While @Idjaw has provide a explanation of a nice way of achieving your output, a simple one line alternative using dict and list comprehension could also be used:

def group(words, keys): 
    return {key: [val for val in words if val.upper() in key] for key in keys}

Explanation:

  • {key: ... for key in keys}: construct a dictionary with the given keys('A', 'B', 'C').
  • [val for val in words if val[0].upper() == key]: and for each key in the dictionary give it the value of the words in words if the word begins with the key. This part creates a filtered list.

EDIT: @Idjaw has informed me that you may not be looking for one-liner, in which case you can use this instead:

def group(words, keys):
    d = {key:[] for key in keys}
    for word in words:
        for key in d:
            if word.upper() in key:
                d[key].append(word)
    return d

But if time is you most major concern, you should use @Idjaw's solution instead of mine.

Output:

>>> group(['apple','orange','banana','berry','corn'], ['A','B','C'])
{'B': ['banana', 'berry'], 'A': ['apple'], 'C': ['corn']}

Note: if you want your dict to be ordered({'A': ['apple'], 'B': ['banana', 'berry'], 'C': ['corn']}), use OrderedDict()

Community
  • 1
  • 1
Christian Dean
  • 22,138
  • 7
  • 54
  • 87
  • I actually did it using `defaultdict` on purpose because it's actually faster than doing it in a comprehension for this particular case. Timing analysis has the comprehension approach at least half a second slower. – idjaw Nov 03 '16 at 04:14
  • @idjaw Yes, i had noticed that. But since the OP did not explicitly mention anything about speed, I wasn't going to assume he was concerned about it. By all means though, use this as another reason that your answer it better than mine ;) – Christian Dean Nov 03 '16 at 04:19
  • Also, why don't you just check `val[0].upper() in keys`? – idjaw Nov 03 '16 at 04:20
  • @idjaw Once again, just me being too lazy to optimize my code. I guess should though, so I'll look over it again. And also, may I ask what method you used to time our code? – Christian Dean Nov 03 '16 at 04:22
  • It would probably not be as easy as just doing a simple `in keys` based on how you are structuring your dict. I'm not deliberately trying to give you hard time, apologies if it is coming across that way. It's just that sometimes, one line expressions aren't always the answer...just because they are one liner solutions. – idjaw Nov 03 '16 at 04:26
  • 1
    @idjaw Sure, I get where your coming from. I know very well that putting things into one line can cause "golf"-ish looking code. In this however, it seemed that a one-liner was a fitting solution. Do you think that a one-liner is too "golf"-ish for something such as this? I could post a solution using for-loops instead. – Christian Dean Nov 03 '16 at 04:30
0

Here's a 1-liner:

def f(list1, list2):
    return { x : [y for y in list1 if y.startswith(x.lower()) ] for x in list 2} 
travelingbones
  • 7,919
  • 6
  • 36
  • 43