0

I hope you can help me with a problem I currently have. I need to build a dictionary with the following structure:

iplist={'32': [ '100.107.0.31/32', '100.107.0.3/32' ]
       ,'24': [ '100.107.0.0/24', '100.107.1.0/24', '100.107.2.0/24' ]
       ,'22': [ '100.107.0.0/22' ]
       ,'20': [ '100.107.0.0/20', '100.107.64.0/20' ]
       ,'16': [ '100.68.0.0/16', '100.69.0.0/16' ]
       ,'0' : [ '0.0.0.0' ] }

Input data will be this and I just need the keys:

netlabels={'100.107.0.31/32': 'aa'
          ,'100.107.0.3/32' : 'bb'
          ,'100.107.0.0/24' : 'cc'
          ,'100.107.1.0/24' : 'dd'
          ,'100.107.2.0/24' : 'ee'
          ,'100.107.0.0/22' : 'ff'
          ,'100.107.0.0/20' : 'gg'
          ,'100.107.64.0/20': 'hh'
          ,'100.68.0.0/16'  : 'hh'
          ,'100.69.0.0/16'  : 'hh'
          ,'0.0.0.0/0'      : 'ii'}

I'm trying to do it with regular expression and list comprehension, because it would be really cool to have it all in one code line. My last 'successful' attempt was this:

>>> import re
>>> netlabels={'100.107.0.31/32' : 'aa'
... ,'100.107.0.3/32' : 'bb'
... ,'100.107.0.0/24' : 'cc'
... ,'100.107.1.0/24' : 'dd'
... ,'100.107.2.0/24' : 'ee'
... ,'100.107.0.0/22' : 'ff'
... ,'100.107.0.0/20' : 'gg'
... ,'100.107.64.0/20': 'hh'
... ,'100.68.0.0/16'  : 'hh'
... ,'100.69.0.0/16'  : 'hh'
... ,'0.0.0.0/0'      : 'ii'}
>>> 
>>> { re.sub(r'^[^/]+/(\d+)$', r'\1', k) : [k] for k in netlabels.keys() }
{'16': ['100.69.0.0/16'], '24': ['100.107.1.0/24'], '22': ['100.107.0.0/22'], '0': ['0.0.0.0/0'], '20': ['100.107.0.0/20'], '32': ['100.107.0.3/32']}
>>> 

But obviously the the lists as values are too short. There were many prefixes just deleted or, to be more precisely, overwritten. What would be the way to push the values on a list and append this list each time a new value needs to be added?

4
  • 3
    "because it would be really cool to have it all in one code line" is a terrible design rationale. Furthermore, your example of the dictionary structure and input data are not valid Python, so it's hard to tell exactly what you want. Commented Mar 18, 2017 at 22:07
  • That's why I added the example from the python console. Commented Mar 18, 2017 at 22:11
  • But ok. You're right. I'm gonna change it. Commented Mar 18, 2017 at 22:14
  • I changed it . I hope it is clearer now. Commented Mar 18, 2017 at 22:20

2 Answers 2

1

It really bears repeating: "it would be cool to have this one one line" is a terrible design rationale. The way to do what you are trying to do is to use a defaultdict. Also, I think you can get away with not using regex.

In [5]: data = {  '100.107.0.31/32' : 'aa',
   ...:   '100.107.0.3/32'  : 'bb',
   ...:   '100.107.0.0/24'  : 'cc',
   ...:   '100.107.1.0/24'  : 'dd',
   ...:   '100.107.2.0/24'  : 'ee',
   ...:   '100.107.0.0/22'  : 'ff',
   ...:   '100.107.0.0/20'  : 'gg',
   ...:   '100.107.64.0/20' : 'hh',
   ...:   '100.68.0.0/16'   : 'hh',
   ...:   '100.69.0.0/16'   : 'hh',
   ...:   '0.0.0.0/0'       : 'ii'}

In [6]: from collections import defaultdict

In [7]: transformed = defaultdict(list)

In [8]: for key in data:
   ...:     _,_,k = key.rpartition('/')
   ...:     transformed[k].append(key)
   ...:

And the results:

In [10]: transformed
Out[10]:
defaultdict(list,
            {'0': ['0.0.0.0/0'],
             '16': ['100.68.0.0/16', '100.69.0.0/16'],
             '20': ['100.107.0.0/20', '100.107.64.0/20'],
             '22': ['100.107.0.0/22'],
             '24': ['100.107.0.0/24', '100.107.1.0/24', '100.107.2.0/24'],
             '32': ['100.107.0.31/32', '100.107.0.3/32']})

You could probably achieve the above on a single line, but it would likely be totally unreadable, and probably would not be anywhere near as efficient.

An alternative to defaultdict is to utilize the setdefault method of vanilla dicts:

In [11]: new_data = {}

In [12]: for key in data:
    ...:     _,_,k = key.rpartition('/')
    ...:     new_data.setdefault(k, []).append(key)
    ...:

In [13]: new_data
Out[13]:
{'0': ['0.0.0.0/0'],
 '16': ['100.68.0.0/16', '100.69.0.0/16'],
 '20': ['100.107.0.0/20', '100.107.64.0/20'],
 '22': ['100.107.0.0/22'],
 '24': ['100.107.0.0/24', '100.107.1.0/24', '100.107.2.0/24'],
 '32': ['100.107.0.31/32', '100.107.0.3/32']}
Sign up to request clarification or add additional context in comments.

Comments

0

Juanpa's answer works, but if you still want to use re and one line code, you can do this:

>>> import re
>>> netlabels={'100.107.0.31/32' : 'aa'
,'100.107.0.3/32' : 'bb'
,'100.107.0.0/24' : 'cc'
,'100.107.1.0/24' : 'dd'
,'100.107.2.0/24' : 'ee'
,'100.107.0.0/22' : 'ff'
,'100.107.0.0/20' : 'gg'
,'100.107.64.0/20': 'hh'
,'100.68.0.0/16'  : 'hh'
,'100.69.0.0/16'  : 'hh'}
>>> dct ={}
>>> {dct .setdefault(re.sub(r'^[^/]+/(\d+)$', r'\1', k), []).append(k) for k in netlabels.keys()}
set([None])
>>> dct
{'24': ['100.107.0.0/24', '100.107.1.0/24', '100.107.2.0/24'], '32': ['100.107.0.31/32', '100.107.0.3/32'], '20': ['100.107.0.0/20', '100.107.64.0/20'], '22': ['100.107.0.0/22'], '16': ['100.69.0.0/16', '100.68.0.0/16']}
>>> 

2 Comments

Technically, this is two lines... two very obscure lines that violate the holy rule of keeping functional constructs separate from state changes.
Thanks to all. Maybe you're right. One line would be hardly readable. I will upvote both answeres.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.