1

I am using Python 3.6 with Django Framework.

I am getting input from the user & that input comes in the form of dictionary in my views.py. Lets say, the user enter 3 values than my dictionary will hold value like

my_dict = {'name1': abc, 'job1': xyz, 'pos1':tak, 'phone1': 12345, 'name2': pqr, 'job2': ftr, 'pos2': lkj, 'phone2': 27654, 'name3': swq, 'job3': nme, 'pos3': mnb, 'phone3': 98421}

I am looking to create sub dictionaries depending on the user input, in above case 3. Expected output is

dict1 = {'name1': abc, 'job1': xyz, 'pos1': tak, 'phone1': 12345}
dict2 = {'name2': pqr, 'job2': ftr, 'pos2': lkj, 'phone2': 27654}
dict3 = {'name3': swq, 'job3': nme, 'pos3': mnb, 'phone3': 98421}

Now, the catch here is we don't know what number of records we get from user, so sub dictionaries also need to be created dynamically. "dict1" to "dictn" where "n" is number given by user. Please suggest!

Jeevan Bodas
  • 148
  • 5
  • 13
  • 3
    You _don't_ want separate named dictionaries for this. You want a collection of dictionaries, that is, you should store those sub-dictionaries in a list or perhaps a dictionary. – PM 2Ring Oct 13 '17 at 07:10
  • Will those keys always be in that format? So we have one of the words ('name', 'job', 'pos', 'phone') followed by a number. Is that correct? Will the number always be a single digit, or will you need to handle bigger numbers? – PM 2Ring Oct 13 '17 at 07:14
  • just calculate the length of the dictionary and then divide then divide by the number of parameters each user can have. – Sargam Modak Oct 13 '17 at 07:16
  • @PM2Ring I am getting the input from user in a dictionary. I am looking for some way to bifurcate the records as shown in the above section named output. Need not be necessarily into 3 separate dictionaries. It could be 3 list or 3 sub dictionaries or anything. – Jeevan Bodas Oct 13 '17 at 07:16
  • @PM2Ring Will those keys always be in that format? "Yes" So we have one of the words ('name', 'job', 'pos', 'phone') followed by a number. Is that correct? "Yes" Will the number always be a single digit, or will you need to handle bigger numbers "Number might be anything but it will be in incremental order like 1,2,3 ... n" where n < 100 – Jeevan Bodas Oct 13 '17 at 07:18
  • 2
    Please see [How do I create a variable number of variables?](https://stackoverflow.com/questions/1373164/how-do-i-create-a-variable-number-of-variables). In particular, read [TigerhawkT3's answer](https://stackoverflow.com/a/38972761/4014959) which explains why it's such a bad idea to create variables dynamically. – PM 2Ring Oct 13 '17 at 11:32
  • 2
    Also see [Why you don't want to dynamically create variables](http://stupidpythonideas.blogspot.com.au/2013/05/why-you-dont-want-to-dynamically-create.html) – PM 2Ring Oct 13 '17 at 11:48
  • Thanks for the links. My issue is, if I am sending data to Salesforce & it doesn't allow DML operation to be performed in loop. So I need separate list or dictionary – Jeevan Bodas Oct 13 '17 at 13:07

4 Answers4

1

You can do the following, using regular expressions to provide itertools.groupby with a key function that uses any sequence of digits at the end of each dict key:

from itertools import groupby
import re

key = lambda s: int(re.search(r'(\d+)$', s).group(1))
d1, d2, d3 = [{k: my_dict[k] for k in g} for _, g in groupby(sorted(my_dict, key=key), key=key)]

This sorts and groups the dictionary keys by any final sequence of digits and builds a list of separate dictionaries from the resulting groups.

user2390182
  • 72,016
  • 6
  • 67
  • 89
1

This code handles any keys starting with letters and ending in a number, which can contain any number of digits. If a key is found that doesn't end in a number it prints an error message, a proper program should do better error handling.

It saves each resulting dictionary into a dictionary sub_dicts, so it doesn't need to sort the input data.

import re

# Make a regex that finds the number
pat = re.compile(r'\d+')

my_dict = {
    'name1': 'abc', 'job1': 'xyz', 'pos1': 'tak', 'phone1': 12345, 
    'name2': 'pqr', 'job2': 'ftr', 'pos2': 'lkj', 'phone2': 27654, 
    'name3': 'swq', 'job3': 'nme', 'pos3': 'mnb', 'phone3': 98421,
    'bad': 'bad_data',
}

# Separate the data based on the trailing number of each key.
sub_dicts = {}
for k, v in my_dict.items():
    m = pat.search(k)
    if m:
        num = m.group()
        sub_dicts.setdefault(num, {})[k] = v
    else:
        print('Invalid key:', k, v)

for k in sub_dicts.keys():
    print(sub_dicts[k])

output

Invalid key: bad bad_data
{'name1': 'abc', 'job1': 'xyz', 'pos1': 'tak', 'phone1': 12345}
{'name2': 'pqr', 'job2': 'ftr', 'pos2': 'lkj', 'phone2': 27654}
{'name3': 'swq', 'job3': 'nme', 'pos3': 'mnb', 'phone3': 98421}
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • Ring Is there any way to get the output in 3 different dictionaries? Now, we are getting them as subdirectories. – Jeevan Bodas Oct 13 '17 at 10:36
  • @JeevanBodas They _are_ 3 different dictionaries. But you can access them via the `sub_dicts` dictionary that they "live" in. As I said at the outset, you _don't_ want them in totally separate variables. How will you handle that when you don't know until run-time if there's going to be 3 of them or 99? It's certainly _possible_ to do it, but there's no good reason to do it, and any Python expert will tell you that way lies madness. ;) – PM 2Ring Oct 13 '17 at 11:22
  • My issue is, if I am sending data to Salesforce & it doesn't allow DML operation to be performed in loop. So I need separate list or dictionary – Jeevan Bodas Oct 13 '17 at 13:10
  • @JeevanBodas Why do you think you need a loop to access any of those sub-dictionaries? If (for example) you want the sub-dict with all the keys ending in '2', you just do `sub_dicts['2']`. – PM 2Ring Oct 13 '17 at 13:19
  • Thanks, I can access it as sub_dicts['1'], sub_dicts['2']... sub_dicts['n'] – Jeevan Bodas Oct 13 '17 at 13:37
0

I wouldn't create separate dictionaries, but either dictionary of dictionaries, or, as suggested in the comment, a list of them. Here's the first approach:

new_dict = dict()
for k, v in my_dict.iteritems():
    k_ = k[-1]
    if not new_dict.get(k_):
        new_dict[k_] = dict()
    new_dict[k_][k] = v

EDIT: To create separate dictionaries (which I don't recommend, though), you can do this:

for k, v in new_dict.iteritems():
    globals()['dict' + k] = v

Now you have dict1, dict2 etc.

Tomáš Linhart
  • 9,832
  • 1
  • 27
  • 39
0

If you want separate dict of each grouped result then you can use generator :

my_dict = {
    'name1': 'abc', 'job1': 'xyz', 'pos1': 'tak', 'phone1': 12345,
    'name2': 'pqr', 'job2': 'ftr', 'pos2': 'lkj', 'phone2': 27654,
    'name3': 'swq', 'job3': 'nme', 'pos3': 'mnb', 'phone3': 98421,
    'bad': 'bad_data',
}
group_dict=dict()
for key,value in my_dict.items():
    if key[-1] not in group_dict:
        group_dict[key[-1]]=[(key,value)]
    else:
        try:
            group_dict[key[-1]].append((key,value))
        except keyError:
            pass

new_dict={}

def groups(x):
    for key,value in x.items():
        yield {x[0]: x[1] for x in value}

new=groups(group_dict)

print(new.__next__())
print(new.__next__())
print(new.__next__())
print(new.__next__())

Output:

{'bad': 'bad_data'}
{'pos2': 'lkj', 'name2': 'pqr', 'job2': 'ftr', 'phone2': 27654}
{'pos1': 'tak', 'name1': 'abc', 'phone1': 12345, 'job1': 'xyz'}
{'pos3': 'mnb', 'phone3': 98421, 'job3': 'nme', 'name3': 'swq'}

If you want all result by one print then :

print(list(groups(group_dict)))

[{'bad': 'bad_data'}, {'pos2': 'lkj', 'name2': 'pqr', 'job2': 'ftr', 'phone2': 27654}, {'pos1': 'tak', 'name1': 'abc', 'phone1': 12345, 'job1': 'xyz'}, {'pos3': 'mnb', 'phone3': 98421, 'job3': 'nme', 'name3': 'swq'}]
Aaditya Ura
  • 12,007
  • 7
  • 50
  • 88