简体   繁体   中英

Build key value array from nested JSON

I have the following JSON object. I need to convert it in javascript into a one-level associative array. (key-value pairs) I've written a recursive function but can't seem to get it to work the way I want it to. Kindly refer to the example below for what I've written:

{
    "Title" : "Test Match",
    "Players" : [
        {
            "ID" : 1,
            "Name" : "Ma Couella",
            "Results" : [2,2,0],
            "TeamMates" : [
                {
                    "ID" : 2,
                    "Age" : 24,
                    "LikesToWin" : false
                }
            ]
        }, {
            "ID" : 22,
            "Name" : "Shawn Kato",
            "Results" : [2,1,0],
            "TeamMates" : [
                {
                    "Name" : "Gerald Anderson",
                    "Age" : 24,
                    "LikesToWin" : false
                }
            ]
        }
    ],
    "Referees" : [
        {
            "ID" : 10,
            "Name" : "Janice Tieu",
            "YearsAsReferee" : 10,
            "EmploymentHistory" : [
                {
                    "JobTitle" : "Senior Referee",
                    "YearsOnTheJob" : 20,
                    "FavouriteMatchesRefereeed" : [
                        {
                            "Title" : "Duran vs Leonard 1",
                            "Year" : 1992,
                            "Description" : "I was refereeing the test match but I put on make up so Duran lost the bout."
                        }, {
                            "Title" : "Duran vs Leonard 2",
                            "Year" : 1994,
                            "Description" : "I was refereeing the second match but I put on make up so Duran lost another bout."
                        }
                    ]
                }, {
                    "JobTitle" : "Juniour Refereee",
                    "YearsOnTheJob" : 3,
                    "FavouriteMatchesRefereeed" : [
                        {
                            "Title" : "Mexican Figher Jones vs Anna Himley",
                            "Year" : 1972,
                            "Description" : "I coached this match. Hehe."
                        }, {
                            "Title" : "Jennifer from the block 2",
                            "Year" : 1982,
                            "Description" : "I coached this other match. Hehe."
                        }
                    ]
                }
            ]
        }
    ]
}

Example of expected associative array below:

[
    "Title" => "Test Match",
    "Players[0][ID]" => 0,
    // rest of the array...
    "Players[0][Teammates][0][ID]" => 2,
    // rest of the array...
    "Referees[0][EmploymentHistory][FavouriteMatchesRefereeed][Title]" => "Duran vs Leonard 1"
]

Here is what I've done so far (javascript)

function converJSONToStrings(values, prevParent){
    console.log(values);
    var result = [];    

    for (var key in values)
    {
        var value = values[key];
        if(value.constructor === Array) {
            // console.log("Found object array in key", key );
            for(var x = 0; x<value.length; x++){
                result[key + '[' + x + ']'] = converJSONToStrings(value[x]);
            }
        } else if (typeof value == 'object') {
            for(var x in value){
                result[key + '[' + x + ']'] = converJSONToStrings(value[x]);
            }
        } else {
            result[key] = value;
        }
    }    

    return result;
}

Finally came up with a solution! Code below...

 /** 
  * Formats the JSON result to an associative array, concatenating child and parent keys 
  */
 var convertJSONAssoc = function(obj, firstlevel) {
     // consider string, number and boolean values in JSON as the last
     // elements and can't be recursed into any further
     if (typeof obj == 'string' || typeof obj == 'number' || typeof obj == 'boolean') {
         var ab = [];
         var bc = {};
         bc.key = '';
         bc.val = obj;
         ab.push(bc);
         return ab;
     }

     // the top most call which builds the final result
     if (firstlevel) {
         var result = {};
         for (key in obj) {
             var val = obj[key];
             var s = convertJSONAssoc(val, false);
             for (var o = 0; o < s.length; o++) {
                 var v = s[o];
                 result[key + v['key']] = v['val'];
             }
         }
         return result;
     } else {
         // this is where the recursion happens. As long as objects are found,
         // we use the code below to keep on parsing obj keys
         var paths = [];
         for (var key in obj) {
             var val = obj[key];
             var s = convertJSONAssoc(val, false);
             for (var o = 0; o < s.length; o++) {
                 var v = s[o];
                 var de = {};
                 de.key = "[" + key + "]" + v['key'];
                 de.val = v['val'];
                 paths.push(de);
             }
         }
         return paths;
     }
 }

This is an old question that came back up because of edits from the OP. Modern techniques make this a bit easier than it was when the question was written.

This uses two functions. pathEntries I use to turn objects into a similar format that I find more useful, something like

[
  [['Title'], 'Test Match'],
  [['Players', 0, 'ID'], 1],
  [['Players', 0, 'Name'], 'Ma Couella,
  // ...
  [['Referees', 0, 'EmploymentHistory', 1, 'FavouriteMatchesRefereeed', 1, 'Year'], 1982],
  [['Referees', 0, 'EmploymentHistory', 1, 'FavouriteMatchesRefereeed', 1, 'Description'], 'I coached this other match. Hehe.']
]

Then convert , with a combination of key mapping and Object.fromEntries (which is easy to shim if you need to support environments without it), turns this into your required format. It looks like this:

 const pathEntries = (obj) => Object (obj) === obj ? Object .entries (obj) .flatMap ( ([k, x]) => pathEntries (x) .map (([p, v]) => [[k, ... p], v]) ) : [[[], obj]] const convert = (obj) => Object .fromEntries ( pathEntries (obj) .map (([k, v]) => [ k[0] + k .slice (1) .map (n => `[${n}]`) .join (''), v ]) ) const data = {Title: "Test Match", Players: [{ID: 1, Name: "Ma Couella", Results: [2, 2, 0], TeamMates: [{ID: 2, Age: 24, LikesToWin: !1}]}, {ID: 22, Name: "Shawn Kato", Results: [2, 1, 0], TeamMates: [{Name: "Gerald Anderson", Age: 24, LikesToWin: !1}]}], Referees: [{ID: 10, Name: "Janice Tieu", YearsAsReferee: 10, EmploymentHistory: [{JobTitle: "Senior Referee", YearsOnTheJob: 20, FavouriteMatchesRefereeed: [{Title: "Duran vs Leonard 1", Year: 1992, Description: "I was refereeing the test match but I put on make up so Duran lost the bout."}, {Title: "Duran vs Leonard 2", Year: 1994, Description: "I was refereeing the second match but I put on make up so Duran lost another bout."}]}, {JobTitle: "Juniour Refereee", YearsOnTheJob: 3, FavouriteMatchesRefereeed: [{Title: "Mexican Figher Jones vs Anna Himley", Year: 1972, Description: "I coached this match. Hehe."}, {Title: "Jennifer from the block 2", Year: 1982, Description: "I coached this other match. Hehe."}]}]}]} console .log (convert (data))
 .as-console-wrapper {max-height: 100% !important; top: 0}

Note that I often write pathEntries in terms of simpler getPaths and path functions. That is cleaner but less efficient as it requires extra traversals of the object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM