the digitalmouse
the digitalmouse

Reputation: 222

Elegant, concise way to reformat JSON data in Javascript?

I receive a JSON result message in the following format from an old database query that I do not have the ability to change at this time:

{
  "vsm1": "2429",
  "vsm2": "2488",
  "vsm3": "1968",
  "vsm4": "",
  "vsm5": "",
  "vsm6": "",
  "vsm7": "",
  "vsm8": "",
  "vsm9": "",
  "vsm10": "",
  "color1": "5",
  "color2": "4",
  "color3": "4",
  "color4": "0",
  "color5": "0",
  "color6": "0",
  "color7": "0",
  "color8": "0",
  "color9": "0", 
  "color10": "0",
  "p1mtime": "1549296004",
  "p2mtime": "1549296009",
  "p3mtime": "1549296014",
  "p4mtime": "",
  "p5mtime": "",
  "p6mtime": "",
  "p7mtime": "",
  "p8mtime": "",
  "p9mtime": "",
  "p10mtime": "",
  "inch1": "",
  "inch2": "",
  "inch3": "",
  "inch4": "",
  "inch5": "",
  "inch6": "",
  "inch7": "",
  "inch8": "",
  "inch9": "",
  "inch10": "" 

}

I would like to re-format it to a more useable object, like so:

{ id: 1, vsm: 2429, color: 5, pmtime: 1549296004, inch: 0  }
{ id: 2, vsm: 2488, color: 4, pmtime: 1549296009, inch: 0  }
{ id: 3, vsm: 1968, color: 4, pmtime: 1549296014, inch: 0  }

...and so on.

The incoming data is currently limited to ten of each 'section' (vsm1, vsm2, ...vsm10, color1, color2, ...color10, etc.), so a static loop of some sort over the ten elements in each section is how i started, but seemed rather ugly and certainly not flexible.

A smart snippet that would handle n-number of elements in each section would be even better just in case the data goes beyond ten elements, or drops to just three (due to absence of data or pruned data).

I'm thinking of something along the lines of using .forEach(), but admittedly my JSON / Object manipulation skills are rather poor, so I turn to the community in the hope that someone can point me in the right direction or knows of a cool, tight routine/function that achieves what I'm looking for. Thanks in advance for any insights.

Upvotes: 2

Views: 244

Answers (4)

Najarana
Najarana

Reputation: 71

I now see that my answer is basically the same as Ninas, haven't seen templating before so that was cool, but seeing as this i the first time i've tried to answer something here I'll just share it anyway.

As Ninas this can handle any length of data.

const data = {"vsm1": "2429",
  "vsm2": "2488",
  "vsm3": "1968",
  "vsm4": "",
  "color1": "5",
  "color2": "4",
  "color3": "4",
  "color4": "0",
  "p1mtime": "1549296004",
  "p2mtime": "1549296009",
  "p3mtime": "1549296014",
  "p4mtime": "",
  "inch1": "",
  "inch2": "",
  "inch3": "",
  "inch4": "",
  };


const vsmRegex = new RegExp("(vsm\\d)");
const keys = Object.keys(data);

const result = [];
let id= 1;

for(let i = 0; i < keys.length; i++) {
  if(keys[i].match(vsmRegex)) {
    let object = {
      id: id,
      vsm: Number(data[`vsm${id}`]) || 0,
      color: Number(data[`color${id}`]) || 0,
      pmtime: Number(data[`p${id}mtime`]) || 0,
      inch: Number(data[`inch${id}`]) || 0
    };
    result.push(object);
    id++;
  } else {
    break;
  }
}

console.log(result);

Upvotes: 1

Scott Sauyet
Scott Sauyet

Reputation: 50787

This solution has a different flexibility than the one from Nina Sholz. Nina's allows you to match any style of number-containing key. But it also requires you to add a template in order to do so. Mine will handle any keys which contain only a single run of digits but nothing more complex. But it doesn't require you to do anything to handle such templates.

const reformat = data => Object.values(Object.keys(data)
  .reduce(
    (a, k, i, _, d = k.match(/\d+/)[0])  => ({
      ...a, 
      [d]: {...(a[d] || {id: Number(d)}), [k.replace(/\d+/, '')]: data[k]}
    }), {})).sort((a, b) => a.id - b.id)

const data = {"vsm1":"2429","vsm2":"2488","vsm3":"1968","vsm4":"","vsm5":"","vsm6":"","vsm7":"","vsm8":"","vsm9":"","vsm10":"","color1":"5","color2":"4","color3":"4","color4":"0","color5":"0","color6":"0","color7":"0","color8":"0","color9":"0","color10":"0","p1mtime":"1549296004","p2mtime":"1549296009","p3mtime":"1549296014","p4mtime":"","p5mtime":"","p6mtime":"","p7mtime":"","p8mtime":"","p9mtime":"","p10mtime":"","inch1":"","inch2":"","inch3":"","inch4":"","inch5":"","inch6":"","inch7":"","inch8":"","inch9":"","inch10":""}

console.log(reformat(data))

I have no idea if you need either sort of flexibility, but these are interesting alternatives to one another.

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386520

You could take an array of the wanted keys with a placeholder for the running number and build new object and push them to the result set.

var data = { vsm1: "2429", vsm2: "2488", vsm3: "1968", vsm4: "", vsm5: "", vsm6: "", vsm7: "", vsm8: "", vsm9: "", vsm10: "", color1: "5", color2: "4", color3: "4", color4: "0", color5: "0", color6: "0", color7: "0", color8: "0", color9: "0", color10: "0", p1mtime: "1549296004", p2mtime: "1549296009", p3mtime: "1549296014", p4mtime: "", p5mtime: "", p6mtime: "", p7mtime: "", p8mtime: "", p9mtime: "", p10mtime: "", inch1: "", inch2: "", inch3: "", inch4: "", inch5: "", inch6: "", inch7: "", inch8: "", inch9: "", inch10: "" },
    keys = ['vsm*', 'color*', 'p*mtime', 'inch*'],
    result = [],
    id = 1;

while (keys[0].replace('*', id) in data) {
    result.push(Object.assign(
        { id },
        ...keys.map(k => ({ [k.replace('*', '')]: +data[k.replace('*', id)]  || 0 }))
    ));
    id++;
}

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

With template literals

var data = { vsm1: "2429", vsm2: "2488", vsm3: "1968", vsm4: "", vsm5: "", vsm6: "", vsm7: "", vsm8: "", vsm9: "", vsm10: "", color1: "5", color2: "4", color3: "4", color4: "0", color5: "0", color6: "0", color7: "0", color8: "0", color9: "0", color10: "0", p1mtime: "1549296004", p2mtime: "1549296009", p3mtime: "1549296014", p4mtime: "", p5mtime: "", p6mtime: "", p7mtime: "", p8mtime: "", p9mtime: "", p10mtime: "", inch1: "", inch2: "", inch3: "", inch4: "", inch5: "", inch6: "", inch7: "", inch8: "", inch9: "", inch10: "" },
    templates = [id => `vsm${id}`, id => `color${id}`, id => `p${id}mtime`, id => `inch${id}`],
    result = [],
    id = 1;

while (templates[0](id) in data) {
    result.push(Object.assign(
        { id },
        ...templates.map(t => ({ [t('')]: +data[t(id)]  || 0 }))
    ));
    id++;
}

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 6

JasonR
JasonR

Reputation: 401

Try this, with oldObject the object you want to clean:

var cleanedObject = {};
for (let [key, value] of Object.entries(oldObject)) {
  let index = key.match('[0-9]+');
  cleanedObject[index] = cleanedObject[index] || {};
  cleanedObject[index][key.replace(index, '')] = value;
}

The result will be an object where cleanedObject['1'] = { vsm: 2429, color: 5, pmtime: 1549296004, inch: '' }, and so on.

Upvotes: 2

Related Questions