susdu susdu - 25 days ago 7
Python Question

python string variable length splitting with nested dict

I have a nested dict in the form of :

dict : {
NAME1 :{
FIELDS :{
FIELD1 :{
MAXBIT:2, MINBIT:0},
FIELD2 :{
MAXBIT:4, MINBIT:3},
FIELD3 :{
MAXBIT:7, MINBIT:5}}}
NAME2
....
}


I want to write a function that takes a
NAME
and a
hex
value, and returns binary decomposition of
hex
according to
dict
.

Example:
for input
f(NAME1, 0x0f)
(
0x0f
is 00001111) and the above dict, output will be:

NAME1:
FIELD1: 111
FIELD2: 01
FIELD3: 000


binary string is composed of fields, each field has it's length in bits.
pretty printing is not important, I'm just looking for the pythonic way to do it. Here's what I wrote:

def f(name1, hex_value):
fields = []
for field, value in dictionary[name1]['FIELDS'].items():
fields.append([value['MINBIT'], value['MAXBIT'], field])
fields.sort()
binary_string = '{0:b}'.format(int(hex_value, 16))
chunks = [bit_pair[1]-bit_pair[0]+1 for bit_pair in fields]
rev_bin_sring = reversed(binary_string)
sliced_fields = [''.join(islice(rev_bin_sring, i)) for i in chunks]
sliced_fields = [string.replace('', '0') for string in sliced_fields]
final_res = [list((field[2], bin_value)) for bin_value, field in zip(sliced_fields, fields)]


but it looks pretty ugly, I'm sure there's a more elegant way.

Answer

I've renamed your dict as d since dict is a builtin python object type and you really shouldn't be overwriting those with your own variables:

d = {
   'NAME1' :{
       'FIELDS' :{
           'FIELD1' :{
               'MAXBIT':2, 'MINBIT':0},
           'FIELD2' :{
               'MAXBIT':4, 'MINBIT':3},
           'FIELD3' :{
               'MAXBIT':7, 'MINBIT':5}}}}

def f(name, hex_value):
    fields = d[name]['FIELDS'] 
    bin_value = bin(hex_value).replace('b','0')[::-1]
    new_d = {name:{}} 
    for field, value in fields.items():
        max_b, min_b = value['MAXBIT']+1, value['MINBIT']
        bin_value += '0'*(max_b-len(bin_value))
        new_d[name][field] = bin_value[min_b:max_b][::-1]
    return new_d

This first gets the relevant fields based on the name argument from the dictionary d. Then it uses the bin function to convert the hex_value to a binary representation (as a string). The first two characters of bin_value will be 0b so replace the b with another 0 and reverse the string (this will allow you to get your values using string slices later on). Now, for each MAXBIT, MINBIT pair you can slice your bin_value string and then reverse it (using [::-1]) to get your intended decomposition. Obviously this will fail if your bin_value is shorter than the max_b value. To accommodate this, you can pad extra '0's on the end of bin_value so that bin_value is at least max_b characters long. Also note that max_b = value['MAXBIT'] + 1 since you want to include the character at the index of max_b but python slices exclusively, so you add 1.

>>> f('NAME1',0x0f)
{'NAME1': {'FIELD1': '111', 'FIELD2': '01', 'FIELD3': '000'}}

EDIT

A slightly different alternative that doesn't require all that reversing back and forth:

def f(name, hex_value):
    fields = d[name]['FIELDS'] 
    bin_value = bin(hex_value).replace('b','0')
    new_d = {name:{}}
    for field, value in fields.items():
        max_b, min_b = value['MAXBIT']+1 , value['MINBIT']
        new_d[name][field] = bin_value.zfill(max_b)[-max_b:-min_b or None]
    return new_d

>>> f('NAME1',0x0f)
{'NAME1': {'FIELD1': '111', 'FIELD2': '01', 'FIELD3': '000'}}

Note: the value['MINBIT'] or None handles the case where value['MINBIT'] is zero.