0

I have the following dictionary:

SEC_DICT = {
    'equitiesa': 'MC',
    'equitiesab': 'MC',
    'etfsa': 'ETF',
    'etfsab': 'ETF',
    'etfsabc': 'ETF',
    'maba': 'MA',
    'mabab': 'MA',
}  

I want to edit or create a new dictionary that that everything starting with equities for example maps to MC, so something like SEC_DICT['equitiesblahblahblah'] would map to MC. Similar with etf and mab in the above dictionary.

The one catch is that the SEC_DICT is referrenced in many many places, so I would ideally not want to create something separate, because this would mean changing things in all places which reference this dictionary.

Is this possible?

For example, if I have the following function:

classify_sec(): 
    a = 'equitieshelloworld' 
    b = 'equitiesblahblahblah'
    y = SEC_DICT[a] 
    z = SEC_DICT[b] 

    return y, z 

I would expect the above to return MC, MC.

May a dictionary is NOT the right data_structure, because I don't want to list out all of the possibilities as keys, because in fact I don't know what the input is, I just want a generic structure where the mapping is something like: 'equities....' -> 'MC' for example.

ifrj
  • 91
  • 7
  • I'm assuming the definition of the dictionary itself is in your code, so you have control over how it is defined (just not over how and where it's used)? – Grismar Jul 03 '23 at 13:15
  • Does this answer your question? [How to make a dictionary that returns key for keys missing from the dictionary instead of raising KeyError?](https://stackoverflow.com/questions/6229073/how-to-make-a-dictionary-that-returns-key-for-keys-missing-from-the-dictionary-i) (insert your own lookup logic instead of returning the key), or https://stackoverflow.com/questions/5808970/custom-dictionary-lookup-in-python – mkrieger1 Jul 03 '23 at 13:16
  • I don't think it's possible without "creating something separate". Either you have to change the code that uses the dictionary, or you have to change the dictionary. – mkrieger1 Jul 03 '23 at 13:18
  • Can you explain why `equitiesblahblahblah` would map to `MC` exactly? I understand that it's because "both start with 'equities'" but would you want to return the value of the key that has the longest match? The first match beyond a certain length? The first key that has a matching prefix at all? – Grismar Jul 03 '23 at 13:20
  • It is certainly possible. What would the output look like if, in the original dictionary, you had 'mabac': 'FOO' as well as the other key/value pairs shown in the question. For clarity, please show the expected output – DarkKnight Jul 03 '23 at 13:35
  • I think the question is being misunderstood, let me give an example. Apologies – ifrj Jul 03 '23 at 13:47
  • Maybe you can't do this after all (reliably). You seem to be looking for common preambles to the keys about which you know nothing. If you know you're looking for keys beginning with, for example, 'equities' then it's easy to identify the keys. But if you have, for example, 'equitiesA': 'FOO' and 'equitiesB': 'BAR' you have a common prefix but different values. How would you propose to handle that? – DarkKnight Jul 03 '23 at 14:03
  • Great point, so I know that they will all have one of a few prefixes like equities, mab, etf; there are 8 in total. – ifrj Jul 03 '23 at 14:08

3 Answers3

1

Why not just have a function? Something like:

def SEC_DICT(key: str) -> str:
    values = {
        'equities': 'MC',
        'etf': 'ETF',
        'mab': 'MA',
    }
    for k in values:
        if key.startswith(k):
            return values[k]
    return ""

In the values dictionary, you provide what the "key" has to start with and what it should return. If the value isn't found, it returns an empty string.

Tini4
  • 48
  • 6
0

Here is a solution that only requires updating the definition of the dictionary with a class you can include - any place that uses the dict will now work with partial keys (longest matching prefix):

from warnings import warn


class KeyPrefixDict(dict):
    def __getitem__(self, item):
        if not isinstance(item, str):
            warn(f"Key {item} is not a string")
            return super().__getitem__(item)
        else:
            # find the key that shares the longest prefix with item
            b_n, b_key = -1, ''
            for key in self.keys():
                n = 0
                for x, y in zip(key, item):
                    if x == y:
                        n += 1
                    else:
                        break
                if n > b_n:
                    b_n, b_key = n, key
                    # if the length of the prefix is the length if item, this is the best match
                    if n == len(item):
                        break
            # return the item at the best found key, or the item at item if no key with matching prefix was found
            return super().__getitem__(b_key if b_key else item)


SEC_DICT = KeyPrefixDict({
    'equitiesa': 'MC',
    'equitiesab': 'MC',
    'etfsa': 'ETF',
    'etfsab': 'ETF',
    'etfsabc': 'ETF',
    'maba': 'MA',
    'mabab': 'MA',
})

print(SEC_DICT['equities'])
print(SEC_DICT['mababcdefg'])
print(SEC_DICT['etfxyz'])
print(SEC_DICT['xyz'])  # longest shared prefix is '', so first item is matched
print(SEC_DICT[''])  # same

Output:

MC
MA
ETF
MC
MC

If you don't like the last two results and would prefer for a KeyError to be thrown when there is no matching prefix at all, change this line:

b_n, b_key = -1, ''

To:

b_n, b_key = 0, item

You can of course opt to leave the warn out altogether.

Grismar
  • 27,561
  • 4
  • 31
  • 54
-1

Use a for loop to iterate through the dictionary, something like this:

SEC_DICT = {
    'equitiesa': 'MC',
    'equitiesab': 'MC',
    'etfsa': 'ETF',
    'etfsab': 'ETF',
    'etfsabc': 'ETF',
    'maba': 'MA',
    'mabab': 'MA',
}

for key in SEC_DICT.keys():
    if key.startswith('equities'):
        SEC_DICT[key] = 'equ'
    elif key.startswith('etfs'):
        SEC_DICT[key] = 'etf'
    elif key.startswith('mab'):
        SEC_DICT[key] = 'ma'

print(SEC_DICT)

This code iterates through the dictionary, and checks the following conditions:

  • if the key starts with equities, it changes it to equ.
  • if the key starts with etfs, it changes it to etf.
  • if the key starts with mab, it changes it to ma.
Ben the Coder
  • 539
  • 2
  • 5
  • 21