简体   繁体   中英

Create custom JSON from array of objects

Using angularjs here:

I have array of object as below. Below it just example showing 2 objects but it can me more than 2.

[
 {
  "name": "John",
  "control": "1",
  "available": "true",  
  "comment": "n1",
  "value": "v1"
 },
 {
  "name": "Peter",  
  "control": "2",
  "available": "true",  
  "type": "integer",
  "comment": "n2",
  "value": "v2",
   "userOptions": [
   {
    "text": "Utah",
    "value": "UT"    
   },
   {
    "text": "New York",
    "value": "NY"
   }
  ]
 }
]

I want to create a json as:

"data":
{
  "John": 
   {
     "control": "1",
     "features": {
          "available": true
      },
      "value": "v1",
      "comment": "c1"
   } ,
 "Peter": 
   {
     "control": "2",
     "features": {
          "available": true,
          "userOptions": {
              "Utah": "UT",
              "New York": "NY",
          }
      },
      "value": "v2",
      "comment": "c2"
   }
}

So if you see the json structure depends on control key because of which some extra param will be added. While some of the keys like control,value, comment remains same.

Could anyone share how can I create the above json. I want to create above in my button event.

$scope.submit = function (isValid) {

if(isValid)
{
   $scope.data.filter(function (item, index) {

      for (var key in item) {

        }                   
   });
}

First, you should note that the first structure supports multiple users with the same name, while the second (wanted one) don't. Anyway, Array.reduce() could be an option for you if you can be sure that names are unique.

Example:

 const input = [{"name":"John","control":"1","available":"true","comment":"n1","value":"v1"},{"name":"Peter","control":"2","available":"true","type":"integer","comment":"n2","value":"v2","userOptions":[{"text":"Utah","value":"UT"},{"text":"New York","value":"NY"}]}]; let res = input.reduce((acc, user) => { let {name, available, userOptions, ...others} = user; acc[name] = {...others, features: {available: JSON.parse(available)}}; if (userOptions !== undefined) { acc[name].features.userOptions = {}; userOptions.forEach(({text, value}) => { acc[name].features.userOptions[text] = value; }); } return acc; }, {}); console.log(res); 
 .as-console {background-color:black !important; color:lime;} .as-console-wrapper {max-height:100% !important; top:0;} 

 const data = [ { "name": "John", "control": "1", "available": "true", "comment": "n1", "value": "v1" }, { "name": "Peter", "control": "2", "available": "true", "type": "integer", "comment": "n2", "value": "v2", "userOptions": [ { "text": "Utah", "value": "UT" }, { "text": "New York", "value": "NY" } ] } ]; const convert = items => items.reduce((result, { name, control, value, comment, available, userOptions }) => ({ ...result, [name]: { control, value, comment, features: { available: available === 'true', ...(Array.isArray(userOptions) && { userOptions: userOptions.reduce((options, { text, value }) => ({ ...options, [text]: value }), {}) }) } } }), {}); console.log(convert(data)); 

I would start by breaking this down into smaller functions. I might end up with a solution like this

 const makeObject = kvs => kvs .reduce ( (a, {text, value} ) => ( {...a, [text]: value} ), {}) const makeFeatures = ( {available, userOptions: uo, ...rest} ) => ( {...rest, features: { ...( available ? {available} : {}), ...( uo ? {userOptions: makeObject (uo) } : {} ) } } ) const transform = (people) => people.reduce( (a, {name, ...rest} ) => ( {...a, [name]: makeFeatures ({...rest}) }), {}) const people = [{"available": "true", "comment": "n1", "control": "1", "name": "John", "value": "v1"}, {"available": "true", "comment": "n2", "control": "2", "name": "Peter", "type": "integer", "userOptions": [{"text": "Utah", "value": "UT"}, {"text": "New York", "value": "NY"}], "value": "v2"}] console .log ( transform (people) ) 
 .as-console {background-color:black !important; color:lime;} .as-console-wrapper {max-height:100% !important; top:0;} 

Thought Process

I would start this process from the outside in, first writing a version of transform like this:

const transform = (people) => 
  people.reduce( (a, {name, ...rest} ) => ( {...a, [name]: {...rest} }), {})

which simply converts people to

{
  John: {...}
  Peter: {...}
}

and those internal object have all the original fields except for name .

Then I would write a version of makeFeatures that looks something like:

const makeFeatures = ( {available, ...rest} ) =>
   ( {...rest, features: {available} } )

so that makeFeatures(people[1]) would result in something like

{
  comment: "n1",
  control: "1",
  features: {
    available: "true"
  },
  value: "v1"
}

and for people[2] it would also include userOptions and type .

Then I can adjust transform like this:

const transform = (people) => 
  people.reduce( (a, {name, ...rest} ) => ( {...a, [name]: makeFeatures({...rest}) }), {})

so that the main function will create our name-based object with available extracted into features for each person.

Now that available is there, we need to handle userOptions as well. This one is slightly more complicated, as it may or may not be included. So I change makeFeatures like this:

const makeFeatures = ( {available, userOptions, ...rest} ) =>
   ( {...rest, features: {available, ...( userOptions ? {userOptions} : {} )} } )

This includes userOptions inside features only if it's in the person object. Seeing this makes me wonder if only if it's in the person object. Seeing this makes me wonder if available` might also only sometimes be included, and to be safe, I will rewrite like this:

const makeFeatures = ( {available, userOptions, ...rest} ) =>
   ( {...rest, features: {
     ...( available ? {available} : {}),
     ...( userOptions ? {userOptions} : {} )
   } } )

So now the only thing left to do is convert the userOptions object from [{text, value}, {text2, value2}, ...] into {text: value, text2: value2, ...} . We write a new function for that:

const convertOptions = userOptions => 
  userOptions .reduce ( (a, {text, value} ) => ( {...a, [text]: value} ), {})

and we alter makeFeatures to use it:

const makeFeatures = ( {available, userOptions, ...rest} ) =>
   ( {...rest, features: {
     ...( available ? {available} : {}),
    ...( userOptions ? {userOptions: convertOptions (userOptions) } : {} )
   } } )

We now have working code. It meets the specs as we have them so far, and it's fairly easy to see how we could adjust to new requirements.

But there are two bits of cleanup I would still like to do. First, all the uses of "userOptions" inside makeFeatures seems untidy. I would add an abbreviation like this:

const makeFeatures = ( {available, userOptions: uo, ...rest} ) =>
   ( {...rest, features: {
     ...( available ? {available} : {}),
     ...( uo ? {userOptions: convertOptions  (uo) } : {} )
   } } )

This is a minor consideration, and some people would not approve, but I find it easier on the eyes.

Second, we can note that convertOptions has nothing to do with options. It's a generic function that takes an array of text / value pairs and converts them into an object. So I would rename that to makeObject , and possibly move it from this block to some utility code.

While this all sounds long and involved, all these steps together took under ten minutes. I was running it in a REPL with instant feedback of my changes, and it really wasn't complicated. Writing it up here took much more time.

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