0

i have an array of object. every object has a date. I want to create a new array of object, grouping by weeks. here is some code example:

const data = [
  {
    "id": 1,
    "status": 1,
    "createdAt": "2022-05-01T08:28:36.284Z"
  },
  {
    "id": 2,
    "status": 2,
    "createdAt": "2022-05-02T07:17:11.724Z"
  },
  {
    "id": 3,
    "status": 3,
    "createdAt": "2022-05-10T07:03:44.465Z"
  },
  {
    "id": 4,
    "status": 3,
    "createdAt": "2022-05-11T16:17:48.863Z"
  }
]

The result I want Is an array that divides object by weeks like:

const newData = [
  {
    "week": 1,
    "status": 1,
    "status": 2
  },
  {
    "week": 2,
    "status": 3,
    "status": 3
  }]

is it possible? can I have same property 2 times in the same object? thank you

devzero
  • 2,510
  • 4
  • 35
  • 55
nicolò
  • 1
  • 1
  • Hi. May I know why you need the same key multiple times in one object? – Mr.Online Jul 11 '22 at 09:49
  • @Mr.Online sure! because I want display that array of object in a nivo bar chart. I'll want weeks on x axis and status on y axis – nicolò Jul 11 '22 at 09:55
  • you should make status an array itself – MWO Jul 11 '22 at 09:57
  • btw you cannot have the same property twice in same object – cmgchess Jul 11 '22 at 09:57
  • No you can't have multiple properties with the same key on an object. If it were possible, how would you access the values? What would you get if you used it like `object.status`? Which status would be changed if you had `object.status = 4`? It just doesn't work – Dwagh Jul 11 '22 at 09:57
  • I suggest you make an array of objects and then use them to populate the chart? – Mr.Online Jul 11 '22 at 09:59
  • you cant have same key more than once in an object. to achieve your goal, you can create a new object with different status keys, like : {status_1: number, status_2: another number} so after that you can create your chart – Deniz Karadağ Jul 11 '22 at 10:02
  • The resulting data structure could be `{week: [statuses]}`. – RobG Jul 11 '22 at 10:31

3 Answers3

0

You should import and use moment.js to find the week number, for charting purposes I would suggest something like this:

const data = [
  {
    "id": 1,
    "status": 1,
    "createdAt": "2022-05-01T08:28:36.284Z"
  },
  {
    "id": 2,
    "status": 2,
    "createdAt": "2022-05-02T07:17:11.724Z"
  },
  {
    "id": 3,
    "status": 3,
    "createdAt": "2022-05-10T07:03:44.465Z"
  },
  {
    "id": 4,
    "status": 3,
    "createdAt": "2022-05-11T16:17:48.863Z"
  }
]
console.log(data.map(a => {
        return {
        status : a.status,
        week: moment(a.createdAt).week()
    }
    }
))
<script src="https://cdn.jsdelivr.net/momentjs/2.13.0/moment.min.js"></script>

This will return and array like this:

[{
  status: 1,
  week: 19
}, {
  status: 2,
  week: 19
}, {
  status: 3,
  week: 20
}, {
  status: 3,
  week: 20
}]
devzero
  • 2,510
  • 4
  • 35
  • 55
0

In ECMAScript, an object can't have multiple properties with the same name. However, an option is to have an array of objects like:

[{week: weekNo, statuses: [status0, status1, status2, …]}]

Week numbers repeat each year, so you should include the year, perhaps using an ISO 8601 format like 2022W03 that is easy to parse and sorts lexically. That will also deal with dates going over a new year.

Array.prototype.reduce with a function to calculate the week number can do the job:

// Return ISO week number: week starts on Monday,
// first week of year is the one containing 4 Jan or 
// first Thursday of the year
function getWeekNumber(d) {
    let z = n => (n<10? '0' : '') + n; 
    d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
    d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay()||7));
    var yearStart = new Date(Date.UTC(d.getUTCFullYear(),0,1));
    var weekNo = Math.ceil(( ( (d - yearStart) / 86400000) + 1)/7);
    return `${d.getUTCFullYear()}W${z(weekNo)}`;
}

function groupByWeek(data) {
  // Pointer to location of week in array
  let index = {};
  let newData = data.reduce((acc, obj) => {
    let weekNo = getWeekNumber(new Date(obj.createdAt));
    if (!index[weekNo]) {
      index[weekNo] = acc.length;
      acc.push({week: weekNo, statuses: []});
    }
    acc[index[weekNo]].statuses.push(obj.status);
    return acc;
  }, []);
  // Sort by week number
  newData.sort((a, b) => a.week.localeCompare(b.week));
  return newData;
}

let data = [
  {"id": 1, "status": 1, "createdAt": "2022-05-01T08:28:36.284Z"},
  {"id": 2, "status": 2, "createdAt": "2022-05-02T07:17:11.724Z"},
  {"id": 3, "status": 3, "createdAt": "2022-05-10T07:03:44.465Z"},
  {"id": 4, "status": 3, "createdAt": "2022-05-11T16:17:48.863Z"},
  {"id": 5, "status": 1, "createdAt": "2023-01-05T08:28:36.284Z"},
];

console.log(groupByWeek(data))
RobG
  • 142,382
  • 31
  • 172
  • 209
0

As we can't have multiple property properties with the same name in an object you should store status in an array.

let getWeekNbr = date => {
    date = formateDate(date);
    let firstDay = new Date(date.getFullYear(),0,1);
    let nbrDays = Math.floor((date - firstDay) / (24*60*60*1000));
    return Math.ceil((date.getDay() + 1 + nbrDays) / 7);
}

let formateDate = date => new Date(Date.parse(date));

let groupBy = (arr, key) => arr.reduce((rv, x) => {
        (rv[x[key]] = rv[x[key]] || []).push(x);
        return rv;
    }, {});


let formatedData = data.map(d => ({...d, week: getWeekNbr(d.createdAt)}));                                       // calcule week nbr
let groupedData = groupBy(formatedData, 'week');                                                                 // group data by week propertie's value
let result = Object.values(groupedData).map(arr => ({week: arr.at(0).week, status: arr.map(d => d.status)}));    // formating result

console.log(result);

// Output
// [
//     { "week": 18, "status": [1, 2] },
//     { "week": 19, "status": [3] },
//     { "week": 20, "status": [3] }
// ]
nem0z
  • 1,060
  • 1
  • 4
  • 17