1

I am having an array of object (rawData). Each object of this array represents a x-coordinate and y-coordinates of various series (i.e,rawData = [(xi,y1i,y2i,..yni)]). I want to convert it to convertedData, which is an array of the object where each object represents just one series (i.e, convertedData = [[(xi,y1i)], [(xi,y2i)]...[(xi,yni)]])

rawData = [{
    x: 1,
    y1:"1 y1 string", 
    y2:"1 y2 string",
    y3:"1 y3 string",

    yn:"1 yn string",
},{
    x: 2,
    y1:"2 y1 string", 
    y2:"2 y2 string",
    y3:"2 y3 string",

    yn:"2 yn string",
}];

  convertedData = [
    {
      name:"y1",
      data:[
        [1,"1 y1 string"],
        [2,"2 y1 string"],
      ]
    },{
      name:"y2",
      data:[
        [1,"1 y2 string"],
        [2,"2 y2 string"],
      ]
    },{
      name:"y3",
      data:[
        [1,"1 y3 string"],
        [2,"2 y3 string"],
      ]
    },{
      name:"yn",
      data:[
        [1,"1 yn string"],
        [2,"2 yn string"],
      ]
    }
  ];

What is the Javascript way to cleanly attempt this?


Work done by me:

let convertedData = [];
rawData = [{
    x: 1,
    y1:"1 y1 string",
    y2:"1 y2 string",
    y3:"1 y3 string",

    yn:"1 yn string",
},{
    x: 2,
    y1:"2 y1 string",
    y2:"2 y2 string",
    y3:"2 y3 string",

    yn:"2 yn string",
}];

function findDataByName(convertedData, name){
    for (let i=0; i<convertedData.length;++i){
        if(convertedData[i].name === name)
            return convertedData[i].data;
    }
    return temp;
}

function convert() {
    /*initialize the convertedData*/
    Object.keys(rawData[0]).forEach((value)=>{
        if(value==='x') return;
        convertedData.push({
            name:value,//y1
            data:[]//[(xi,y1i)]
        })
    });

    /*now loop over rawData and fill convertedData's data array*/
    rawData.forEach((obj)=>{
        Object.keys(obj).forEach((key)=>{
            if(key==='x') return;
            let data = findDataByName(convertedData,key);
            data.push([obj['x'], obj[key]]);//pushing a new coordinate
        });
    })
}

convert();
console.log(convertedData);

4
  • Not totally clear what relationships are supposed to be. All the strings are the same in each data array in convertedData example Commented Jul 18, 2018 at 20:02
  • @charlietfl Thanks for asking. It was a mistake on my part which I have corrected. data basically an object array where each object represents one coordinate. Commented Jul 18, 2018 at 20:11
  • I find it confusing that you call strings like "1 y1 string" a coordinate... Are they numbers in reality? Commented Jul 18, 2018 at 20:14
  • 1 y1 string is basically (value of x coordinate, name of series, and just static "string")...they are string Commented Jul 18, 2018 at 20:17

3 Answers 3

1

This is a pretty good use case of reduce on the outer array followed by looping over the entries of each object.

let rawData = [{x: 1,y1:"1 y1 string", y2:"1 y2 string",y3:"1 y3 string",yn:"1 yn string",},{x: 2,y1:"2 y1 string", y2:"2 y2 string",y3:"2 y3 string",yn:"2 yn string",}];

let obj = rawData.reduce((o, item) => {
    Object.entries(item).forEach(([key, val]) => {
        if (key === 'x') return
        if (o.hasOwnProperty(key)){
            let data = o[key].data
            data.push([data.length+1, val])
        } else {
            o[key] = {name: key, data: [[1, val]]}
        }
    })
    return o
}, {})

// obj is an object, but you are only interested in the values:
console.log(Object.values(obj))

Sign up to request clarification or add additional context in comments.

Comments

1

It all depends on what you would regard cleanest. There are several programming strategies, each with their pros and cons.

Here is a functional programming approach, using Array prototype functions and an ES6 Map as temporary hash to group the data by x value.

const rawData = [{x: 1,y1:"1 y1 string",y2:"1 y2 string",y3:"1 y3 string",yn:"1 yn string",},{x: 2,y1:"2 y1 string",y2:"2 y2 string",y3:"2 y3 string", yn:"2 yn string", }];

const convertedData = Array.from(rawData.reduce( 
    (map, obj) => Object.keys(obj).filter(key => key != "x").reduce(
        (map, key) => map.set(key, (map.get(key) || []).concat([[obj.x, obj[key]]])),
        map
    ),
    new Map
), ([name, data]) => ({name, data}));

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

2 Comments

That's nice. I didn't realize you could call new Map without the ().
It is the new syntax that allows that.
0

It's not beautiful but it does do the trick. The only really janky part is the Number(item[1][0]) which would strip 1 from ["y2", "1 y2 string"]

//Flatten the data
const y = rawData.map(item => {
  return Object.entries(item).filter(entry => entry[0] !== 'x');
}).reduce((acc, curr) => acc.concat(curr), []);

//Create yheader array: ["y1", "y2"..., "yn"];
const yHeaders = [... new Set(y.map(item => item[0]))];

//for each y header, build the corresponding data object 
const clean = yHeaders.map(targetHeader => {
  return {
    name: targetHeader,
    data: y.filter(item => item[0] === targetHeader).map(item => [ Number(item[1][0]), item])
  }
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.