1

The following code takes two inputs, item_name and sale_type. It will look through item_dict to find if any keys that contain part of the item_name or fully matches it and output the values.

I am trying to convert the following for loop into a list comprehension. I am fairly comfortable with the basic list comprehensions but in this case I require the text to be split and obtain the relevant results. I am not sure if what I am asking for is possible.

item_name = "GalaxyDevices"
sale_type = "buy"
item_dict = {"buy_Galaxy": [11111, 2232], "sell_Galaxy": [2111]}
results = []

for key, value in item_dict.items():
    key = key.split("_")

    if key[0] != sale_type:
        continue

    if key[1] in item_name:
        results.extend(value)

print(results)

input / output:

item_name = "GalaxyDevices"
sale_type = "buy"
>>> [11111, 2232]

My failed attempt:

results = [value.split("_") for key, value in item_dict.items()]

Many thanks!

Raymond C.
  • 572
  • 4
  • 24

2 Answers2

3

I think this should work:

results = [
    value
    for key, values in item_dict.items()
    if (key.split('_')[0] == sale_type
        and key.split('_')[1] in item_name)
    for value in values
]
Timus
  • 10,974
  • 5
  • 14
  • 28
  • That's pretty neat but I don't understand why is values needed at the last line? – Raymond C. Sep 23 '20 at 14:14
  • Otherwise you get a list of lists in return. – Timus Sep 23 '20 at 14:15
  • I really like the solution but the last part is playing with my mind. Does the last for value fall under the if statement? Or is it using the first for loop as a nest? – Raymond C. Sep 23 '20 at 14:21
  • Not exactly: It's only executed once the ```if``` condition is met. EDIT: Sorry, I haven't read your second question: It is using the first ```for``` as a nest but only once the ```if``` condition is met. – Timus Sep 23 '20 at 14:23
  • Noted on that, do you have any reference where I might be able to pick up more on this? – Raymond C. Sep 23 '20 at 14:24
  • Didn't know I could nest ```for``` loop like that, brilliant! – jupiterbjy Sep 23 '20 at 14:28
  • @Raymond C.: I learned a ton form Trey Hunner, a really cool and knowledgeable guy: [link](https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/) – Timus Sep 23 '20 at 14:30
2

Use nested list comprehension and set to determine if any has intersection.

This however requires 3 rules:

  • Name has some kind of separator - CamelCase separation requires re.
  • Part of each name should not match any in dict. i.e. Hello_world matching Hello_buy and world_buy at same time for best result.

For one-liner:

name = input("name >> ")
type_ = input("transaction type >> ")
results = [item_dict[key] for key in [key_ for key_ in item_dict.keys() if type_ in key_] if set(name.split()) & set(key.split("_"))]

Explanation

results = [item_dict[key] for key in
           [key_ for key_ in item_dict.keys() if type_ in key_]
           if set(name.split()) & set(key.split("_"))]

Above equals to:

intersecting_words = lambda name_, keys_: set(name) & set(keys_)

key_matching_part = [key for key in item_dict.keys() if intersecting_words(name, key.split("_"))]

results = [item_dict[key] for key in key_matching_part]

Full test:

item_dict = {"Guido_buy": 100, "Guido_sell": -100,
             "Ramalho_buy": 200, "Ramalho_sell": -200}


name = "Guido van Rossum"
type_ = "buy"

results = [item_dict[key] for key in [key_ for key_ in item_dict.keys() if type_ in key_] if set(name.split()) & set(key.split("_"))]
print(results)


name = "Luciano Ramalho"
type_ = "sell"

results = [item_dict[key] for key in [key_ for key_ in item_dict.keys() if type_ in key_] if set(name.split()) & set(key.split("_"))]
print(results)

Output:

[100]
[-200]
jupiterbjy
  • 2,882
  • 1
  • 10
  • 28
  • That is overly complicated! – AnsFourtyTwo Sep 23 '20 at 13:47
  • 1
    That's what OP requested isn't it? OP clearly mentioned he / she want to get 2 input - *name* and *transaction type*. This is simplest yet shortest answer to satisfy those. – jupiterbjy Sep 23 '20 at 13:50
  • I like the answer here but it's just missing the item_name whereby it can do like a containing match as in my example of the "galaxydevices" and the dictionary has "buy_galaxy" which is managed to be found. This works if I only type the item_name in full. – Raymond C. Sep 23 '20 at 13:53
  • Yeah, I've overseen from your description, that you'll need to process `item_name` as well. Just can be seen from the code provided. – AnsFourtyTwo Sep 23 '20 at 13:55
  • I think you need to separate ```item name``` with some separator, according to [this](https://stackoverflow.com/questions/2277352/split-a-string-at-uppercase-letters) without aid of ```re```. It's not possible with ```listcomp```, but with functions. I'll update answer to accordingly for that case if possible. – jupiterbjy Sep 23 '20 at 13:57
  • Yep. Possible. but that's overly complicated for a single line and may violate PEP-8 with line length. I'll update it anyway. – jupiterbjy Sep 23 '20 at 14:02
  • That one un-readable line will be alot better if it was broken down to a few lines. One-line is code is not necessarily better... – Tomerikoo Sep 23 '20 at 14:07
  • I was thinking of using function as this goes too complex, I'll add some self-explanatory codes too. – jupiterbjy Sep 23 '20 at 14:08
  • I got it working!! Your answer drove me somewhere and I managed to get it to work with the split. – Raymond C. Sep 23 '20 at 14:16
  • I wanted to mark yours as the answer but sadly I tested the newer solution and found it was less complicated and more readable. I upvoted your question and appreciate the effort and time. – Raymond C. Sep 23 '20 at 14:20