Home > Software design >  Parse values from JSON body
Parse values from JSON body

Time:02-05

I have the attached following actions parameter part of my JSON data.

'actions': [{'action_type': 'onsite_conversion.post_save', 'value': '1'},
  {'action_type': 'link_click', 'value': '2'},
  {'action_type': 'post', 'value': '3'},
  {'action_type': 'post_reaction', 'value': '4'},
  {'action_type': 'video_view', 'value': '5'},
  {'action_type': 'post_engagement', 'value': '6'},
  {'action_type': 'page_engagement', 'value': '7'}],

API can send the following options at any time;

action_types = ["onsite_conversion.post_save", "link_click", "post", "post_reaction", "comment", "video_view", "post_engagement", "page_engagement"]

I tried to write a python script that parses these possible values (in action_types list order) from the JSON body, as seen from the sample JSON data it doesn't send comment value so in this case script should write 0 to return list, below is my script

def get_action_values(insight):
    action_types = ["onsite_conversion.post_save", "link_click", "post", "post_reaction", 
                "comment", "video_view", "post_engagement", "page_engagement"]
    action_type_values = []
    
    for action in action_types:
        if action in [item["action_type"] for item in insight["actions"]]:
            for item in insight["actions"]:
                if action in item["action_type"]:
                    if "value" in item:
                        action_type_values.append(item["value"])
                    else:
                        action_type_values.append("Null")
        else:
            action_type_values.append(0)
    return action_type_values

I am expecting it to return as [1,2,3,4,0,5,6,7] but it returned as [1, 2, 1, 3, 4, 6, 4, 0, 5, 6, 7]

CodePudding user response:

Here is a possible solution:
The action_type_values list is initialized with zeros of the length -> len() of action_types which represents the default value for any missing action_type in the insight dictionary.

The for-loop then loops through all the actions for each action, if action_type is in action_types, it's index is found and the corresponding value in the action_type_values is updated with the int(value) of the action.

def get_action_values(insight):
    action_types = ["onsite_conversion.post_save", "link_click", "post", "post_reaction", "comment", "video_view", "post_engagement", "page_engagement"]
    action_type_values = [0] * len(action_types)
    
    for item in insight["actions"]:
        if item["action_type"] in action_types:
            idx = action_types.index(item["action_type"])
            action_type_values[idx] = int(item["value"])
    return action_type_values

print(get_action_values(my_json))

[1, 2, 3, 4, 0, 5, 6, 7]

CodePudding user response:

As you can see in the code you posted and as it was already pointed out in the comments to your question:

{'action_type': 'link_click', 'value': '2'},

'value' has a value of '2' not 2, so if you expect integers in the resulting list use:

if "value" in item:
   action_type_values.append(int(item["value"]))

or to be consistent with the string representation:

else:
    action_type_values.append("0")

Another issue was caused by the if-condition if action in item["action_type"]: you use because you have an action type 'post' which occur in all of 'onsite_conversion.post_save', 'post', 'post_reaction' and 'post_engagement'also containing 'post', so it is necessary to change the condition (as pointed out by barmar in the comments) to: if action == item["action_type"]: to eliminate duplicates in the result.

Below the entire corrected code:

dct_json = {'actions': [
  {'action_type': 'onsite_conversion.post_save', 'value': '1'},
  {'action_type': 'link_click', 'value': '2'},
  {'action_type': 'post', 'value': '3'},
  {'action_type': 'post_reaction', 'value': '4'},
  {'action_type': 'video_view', 'value': '5'},
  {'action_type': 'post_engagement', 'value': '6'},
  {'action_type': 'page_engagement', 'value': '7'}],
}
# API can send the following options at any time;
action_types = ["onsite_conversion.post_save", "link_click", "post", "post_reaction", "comment", "video_view", "post_engagement", "page_engagement"]

def get_action_values(insight):
    action_types = ["onsite_conversion.post_save", "link_click", "post", "post_reaction", 
                "comment", "video_view", "post_engagement", "page_engagement"]
    action_type_values = []
    
    for action in action_types:
        if action in [item["action_type"] for item in insight["actions"]]:
            for item in insight["actions"]:
                if action == item["action_type"]:
                    if "value" in item:
                        action_type_values.append(int(item["value"]))
                    else:
                        action_type_values.append("Null")
        else:
            action_type_values.append(0)
    return action_type_values
print( get_action_values(dct_json) )

printing the expected:

[1, 2, 3, 4, 0, 5, 6, 7]

Animated by Jamiu S. answer which tries to optimize the code but does generally not work properly ( as of 2023-02-05 01:06 CET ) failing with KeyError or not printing all the values, below improved code eliminating one loop and using dictionary.get(key, default_value) syntax to eliminate an if/else code section. The code includes changed input data to show that it covers cases in which the code in Jamiu S. answer fails to work or to work properly. Notice that the returned list items are all strings to be consistent with 'Null' and the 'action_type' values:

dct_json = {'actions': [
  {'action_type': 'onsite_conversion.post_save', 'value': '1'},
  {'action_type': 'link_click',                  'value': '2'},
  {'action_type': 'page_engagement-no-value', 'no-value': '8'},
  {'action_type': 'post',                        'value': '3'},
  {'action_type': 'post_reaction',               'value': '4'},
  {'action_type': 'video_view',                  'value': '5'},
  {'action_type': 'post_engagement',             'value': '6'},
  {'action_type': 'page_engagement',             'value': '7'},
  {'action_type': 'post',                        'value': '33'},
]}
action_types = ["onsite_conversion.post_save", "link_click", "post", 
        "post_reaction", "comment", "video_view", "post_engagement", 
        "page_engagement", "page_engagement-no-value"]

def get_action_values(insight, action_types):
    action_type_values = []
    for action in action_types:
        lstofmatchingitems = [ item for item in insight["actions"] if item["action_type"]==action ]
        if lstofmatchingitems:
            for item in lstofmatchingitems:
                action_type_values.append(item.get("value", "Null"))
        else:
            action_type_values.append("0")
    return action_type_values
print(get_action_values(dct_json, action_types))

prints

['1', '2', '3', '33', '4', '0', '5', '6', '7', 'Null']
  • Related