1

I have a function that returns the following dictionary

abc= {"type":"insecure","id":"1",
       "name":"peter","s_count":"2",
      "b_count":"1", "s_1_name":"melisa",
        "s_1_id":"2","s_2_name":"graham",
      "s_2_id":"4", "b_1_name":"henrik", 
        "b_1_id": "9"}

I want to chage the dictionary in the following way:

xyz={"type":"insecure","id":"1",
     "name":"peter",
      "s" : [{"id" : "2", "name": "melisa"},
             {"id" : "4", "name": "graham"}],
      "b" : [{"id" : "9", "name": "henrik"}]}

The logic is as follows: If there is s_count in dictionary then create a list that contains all the values that are starts with s. for example in my case create a a list which contain different dictionaries with each dictionary containing the s_name and s_id e.g in my case there are two dictionaries in the resulting list:

"s" : [{"id" : "2", "name": "melisa"},{"id" : "4", "name": "graham"}] 

and do the same with b as well if b_count count exists.

can somebody help me out with that?

Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
hjelpmig
  • 1,406
  • 6
  • 19
  • 23
  • 2
    Could there be `a_count` or `c_count` or is it only those two you mentioned? – jamylak May 10 '13 at 15:09
  • no it will only have s_count and b_count or no values that start with prefix s_ or b_. – hjelpmig May 10 '13 at 16:56
  • 1
    I don't agree that this is too localised; voting to reopen. The underlying general problem is how to efficiently process dictionary keys with a common prefix. – Martijn Pieters May 11 '13 at 13:05

2 Answers2

4

I'd use a helper function:

from itertools import groupby
from operator import itemgetter

def extract_keys(mapping, prefix):
    prefix = '{}_'.format(prefix)

    # test for the `.._count` key first, if it's not there, bail out early
    if prefix + 'count' not in mapping:
        return None

    # find all matching keys, split out the counter for sorting and grouping
    keys = [(k, int(k.split('_', 2)[1]))
        for k in mapping if k.startswith(prefix) and k != prefix + 'count']
    keys.sort(key=itemgetter(1))

    # group keys on the counter, then generate a dictionary per counter value
    return [{k[0].split('_', 2)[-1]: mapping[k[0]] for k in group}
        for c, group in groupby(keys, itemgetter(1))]

This function extracts the keys by prefix:

>>> extract_keys(abc, 's')
[{'id': '2', 'name': 'melisa'}, {'name': 'graham', 'id': '4'}]
>>> extract_keys(abc, 'b')
[{'name': 'henrik', 'id': '9'}]

Use that function to create new dictionaries or transform the existing dictionaries in-place:

xyz = {k: v for k, v in abc.iteritems() if not k.startswith('s_') and not k.startswith('b_')}
s_values = extract_keys(abc, 's')
if s_values is not None:
    xyz['s'] = s_values
b_values = extract_keys(abc, 'b')
if b_values is not None:
    xyz['b'] = b_values

This transforms your abc sample input into:

>>> pprint(xyz)
{'b': [{'id': '9', 'name': 'henrik'}],
 'id': '1',
 'name': 'peter',
 's': [{'id': '2', 'name': 'melisa'}, {'id': '4', 'name': 'graham'}],
 'type': 'insecure'}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • thanks for your help. it solved my problem. I have also improved the code a bit to make it more flexible and can handle all split but cant post since the question has been closed :( `code` # find all matching keys, split out the counter for sorting and grouping keys = [(k, int(k.split('_', -1)[-2])) for k in mapping if k.startswith(prefix) and k != prefix + 'count'] keys.sort(key=itemgetter(1)) # group keys on the counter return [{k[0].split('_', -1)[-1]: mapping[k[0]] for k in group} for c, group in groupby(keys, itemgetter(1))] – hjelpmig May 11 '13 at 13:05
0

I have improve it a bit more so that it becomes more flexible.

def extract_keys(mapping, prefix):
prefix = "{}_".format(prefix)

# test for the `.._count` key first, if it's not there, bail out early
if prefix + "count" not in mapping:
    return None

# find all matching keys, split out the counter for sorting and grouping
keys = [(k, int(k.split("_", -1)[-2])) for k in mapping if k.startswith(prefix) and k != prefix + "count"]
keys.sort(key=itemgetter(1))

# group keys on the counter, then generate a dictionary per counter value
return [{k[0].split("_", -1)[-1]: mapping[k[0]] for k in group} for c, group in groupby(keys, itemgetter(1))]

consider the following dictionary.

abc= {"type":"insecure","id":"1",
   "name":"peter","s_a_count":"2",
  "b_count":"1", "s_a_1_name":"melisa",
    "s_a_1_id":"2","s_a_2_name":"graham",
  "s_a_2_id":"4", "b_1_name":"henrik", 
    "b_1_id": "9"}

Use that function to create new dictionaries or transform the existing dictionaries in-place:

xyz = {k: v for k, v in abc.iteritems() if not k.startswith('s_a') and not k.startswith('b_')}
s_values = extract_keys(abc, 's_a')
if s_values is not None:
    xyz['s_a'] = s_values
b_values = extract_keys(abc, 'b')
if b_values is not None:
    xyz['b'] = b_values

and transformed output is:

print xyz

{'b': [{'id': '9', 'name': 'henrik'}],
'id': '1',
'name': 'peter',
's_a': [{'id': '2', 'name': 'melisa'}, {'id': '4', 'name': 'graham'}],
'type': 'insecure'}
hjelpmig
  • 1,406
  • 6
  • 19
  • 23