user1727852
user1727852

Reputation: 143

How to transform object to nested object

I am new in JavaScript and programming. I get data via AJAX. I want to re-generate it to get a nested object grouped by part of the data. In this case I want it grouped by year and month

Here is my data and my function:

myObj = [
    	  {"date":'2019-06-05',"name":"abc 0"},
    	  {"date":'2019-06-01',"name":"abc 1"},
    	  {"date":'2019-05-25',"name":"abc 2"},
    	  {"date":'2019-05-15',"name":"abc 3"},
    	  {"date":'2020-06-30',"name":"abc 4"},
    	  {"date":'2020-06-25',"name":"abc 5"},
    	  {"date":'2020-05-28',"name":"abc 6"},
    	  {"date":'2020-05-26',"name":"abc 7"}
            ];

function regenerate(data) {
  var result = {
    "allyears": [{}]
  };
  for (x = 0; x < data.length; x++) {
    var year = data[x].date.slice(0, 4);
    var month = data[x].date.slice(5, 7);

    if (!result.allyears.months) {
      result.allyears['year'] = year;
      result.allyears.months = [{}];
    }
    if (!result.allyears.months.data) {
      result.allyears.months['month'] = month;
      result.allyears.months.data = [{}];
    }
    result.allyears.months.data[x] = data[x];
  }
  console.log(result);
  return result;
};

regenerate(myObj);

Result I expect:

{
  "allyears": [{
    "year": "2019",
    "months": [{
      "month": "06",
      "data": [{
          "date": '2019-06-05',
          "name": "abc 0"
        },
        {
          "date": '2019-06-01',
          "name": "abc 1"
        }
      ]
    }, {
      "month": "05",
      "data": [{
          "date": '2019-05-25',
          "name": "abc 2"
        },
        {
          "date": '2019-05-15',
          "name": "abc 3"
        },
      ]
    }]
  }]
};

What am I missing in my function?

Upvotes: 1

Views: 205

Answers (1)

briosheje
briosheje

Reputation: 7446

Probably not the cleverest solution, but it should do the job "beautifully". The routine is taking the advantage of Array.reduce, where an initial accumulator (in this case an empty array) is used and, while looping the original myObj array, it checks whether:

  • The year element exists in the array. If it doesn't it creates it.
  • The month element exists in the year element. If it doesn't it creates it.
  • Once everything is created, it adds data to the current month.

I will add some comments to the snippet below for further explanations, the output, to me, seems okay.

const myObj = [
      {"date":'2019-06-05',"name":"abc 0"},
      {"date":'2019-06-01',"name":"abc 1"},
      {"date":'2019-05-25',"name":"abc 2"},
      {"date":'2019-05-15',"name":"abc 3"},
      {"date":'2020-06-30',"name":"abc 4"},
      {"date":'2020-06-25',"name":"abc 5"},
      {"date":'2020-05-28',"name":"abc 6"},
      {"date":'2020-05-26',"name":"abc 7"}
];

let res = {
  allyears: myObj.reduce((acc, next) => {
    let [year, month, day] = next.date.split('-');
    // ^-- Acquire year, month and day (actually, day is not needed) from the original date string.
    let yearRef = acc.find(i => i.year === year);
    // ^-- checks whether the current year already exists in the array.
    if (!yearRef) acc.push({year}), yearRef = acc[acc.length - 1];
    // ^-- if it doesn't, it creates it and fill the above reference of it.
    yearRef.months = yearRef.months || [];
    // ^-- same as the year above, but with month.
    let monthRef = yearRef.months.find(i => i.month === month);
    if (!monthRef) yearRef.months.push({month}), monthRef = yearRef.months[yearRef.months.length - 1]// ^-- same as above, with month.
    monthRef.data = (monthRef.data || []).concat(next);
    // ^-- once the month element is available, add the next element to data. If data does not yet exist, init it.
    return acc;
  }, [])
};

console.log(res);

Upvotes: 1

Related Questions