0

I have the following JSON:

[
{Id: "1", Id2: "1", Year: "2019", Month: "1", Value: "123"},
{Id: "1", Id2: "1", Year: "2019", Month: "2", Value: "3"},
{Id: "1", Id2: "1", Year: "2019", Month: "3", Value: "-123"},
{Id: "1", Id2: "1", Year: "2019", Month: "4", Value: "-4123"},
...
{Id: "1", Id2: "1", Year: "2020", Month: "1", Value: "214"},
{Id: "1", Id2: "1", Year: "2020", Month: "2", Value: "-41123"},
{Id: "1", Id2: "1", Year: "2020", Month: "3", Value: "12344"},
{Id: "1", Id2: "1", Year: "2020", Month: "4", Value: "1232"}
]

Which I'd like to transform into this:

[{ 
  Year : "2019",
    data : [{ 
      Month : "01",
      Value : "123"
    },{
      Month : "02",
      Value : "2223"
    }...
  },{
  Year : "2020",
    data: [{
       Month : "01",
       Value : "214"
    }...
}] 

Not sure how to do this easily, seems it's beyond me right now.

Is anyone able to point me in the right direction?

Many thanks,

Fish

3
  • You want multiple objects as a solution, each for one year or would be a single object that holds all years as key be okay too? Commented Aug 19, 2019 at 13:31
  • 1
    Possible duplicate of Group by array and add field and sub array in main array (I've issued a close vote for another duplicate, which is less clear than this one) Commented Aug 19, 2019 at 13:41
  • Sorry, I did look but couldn't find a similar post. Solved now, thanks Commented Aug 19, 2019 at 13:59

3 Answers 3

1

Try using Array.reduce

Here is a working example assuming there are no duplicate entries for the same month/year combination

var testData = [
{Id: "1", Id2: "1", Year: "2019", Month: "1", Value: "123"},
{Id: "1", Id2: "1", Year: "2019", Month: "2", Value: "3"},
{Id: "1", Id2: "1", Year: "2019", Month: "3", Value: "-123"},
{Id: "1", Id2: "1", Year: "2019", Month: "4", Value: "-4123"},
{Id: "1", Id2: "1", Year: "2020", Month: "1", Value: "214"},
{Id: "1", Id2: "1", Year: "2020", Month: "2", Value: "-41123"},
{Id: "1", Id2: "1", Year: "2020", Month: "3", Value: "12344"},
{Id: "1", Id2: "1", Year: "2020", Month: "4", Value: "1232"}
];

var reducedTestData = testData.reduce((acc, item) => {
	let itemData = {
    Month: item.Month,
    Value: item.Value
  };
	return Object.assign({}, acc, {
  	[item.Year]: acc.hasOwnProperty(item.Year) 
    	? Object.assign(
      	{}, 
        acc[item.Year], 
        {
        	data: acc[item.Year].data.concat(itemData) 
        }
      ) 
      : {data: [itemData]}
  });
}, {});

document.body.appendChild(document.createElement('pre')).innerHTML = JSON.stringify(reducedTestData, null, 2);

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

Comments

0

Here is a working example of a solution:

const source =  [
    {Id: "1", Id2: "1", Year: "2019", Month: "1", Value: "123"},
    {Id: "1", Id2: "1", Year: "2019", Month: "2", Value: "3"},
    {Id: "1", Id2: "1", Year: "2019", Month: "3", Value: "-123"},
    {Id: "1", Id2: "1", Year: "2019", Month: "4", Value: "-4123"},
    {Id: "1", Id2: "1", Year: "2020", Month: "1", Value: "214"},
    {Id: "1", Id2: "1", Year: "2020", Month: "2", Value: "-41123"},
    {Id: "1", Id2: "1", Year: "2020", Month: "3", Value: "12344"},
    {Id: "1", Id2: "1", Year: "2020", Month: "4", Value: "1232"}
];

const destination = []
const classifiedTokens = [] // will be used to stored already classified tokens

const getNextTokenArray = function(source) {
    let unusedToken = null;
    const nextTokenArray = source.filter(function(element) {
        if (!unusedToken && !classifiedTokens.includes(element["Year"])) {
            unusedToken = element["Year"]
            classifiedTokens.push(unusedToken)
        }
        return unusedToken ? unusedToken === element["Year"] : false;
    });
    return unusedToken ? nextTokenArray : null;
}

const addToDestination = function(tokenArray) {
    if (tokenArray.length === 0) return
    let res = {
        Year : tokenArray[0]["Year"],
        data : []
    }
    tokenArray.forEach((element) => {
        res.data.push({
            Month: element.Month,
            Value: element.Value
        })
    })
    destination.push(res);
}

let nextTokenArray = getNextTokenArray(source);
while (nextTokenArray) {
    addToDestination(nextTokenArray)
    nextTokenArray = getNextTokenArray(source)
}

document.body.appendChild(document.createElement('pre')).innerHTML = JSON.stringify(destination, null, 2);

you can try it directly in the following fidle: https://jsfiddle.net/zgj0fcq9/1/

Comments

-1

You can do this with reduce. Reduce is often used to take an array and turn it into a single value, e.g. summing items in an array:

let vs = [1,2,3,4];
console.log(vs.reduce(function(accumulator, value){ return accumulator + value; }, 0));

The second parameter in reduce is the default (or starting) value (in the above that's 0).

So to reduce your array of objects into your array of nested objects, you'll need to check your accumulator to see if the year already exists in the outer array:

let year = accumulator.find(item => item.Year === value.Year);

If year is undefined then you'll need to create a new object with the current year, and give it a data property that's an array with one item that is an object with the Month and Value properties set.

let newYear = { Year: value.Year, data: [{ Month: value.Month, Value: value.Value}]}

Then add that to the accumulator (e.g. with push).

If the year does exist in the accumulator then you'll need to add a new object to the data property:

year.data.push({Month: value.Month, Value: value.Value});

Then you're done.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.