Search code examples
javascriptarrayssortingchart.jsdestructuring

is it possible to sum up the properties of an array of objects and to filter it according to another property?


My Title seems a bit confusing,

Here's my problem, i want to destructure an array of objects in order to use it's properties in a chartJS Line Graph.

Here's a reproduced array:

[{
    "page": "Page 1",
    "date": "2021-10-05",
    "visitors": 10
}, {
    "page": "Page 2",
    "date": "2021-10-05",
    "visitors": 20
}, {
    "page": "Page 3",
    "date": "2021-10-05",
    "visitors": 30
},{
    "page": "Page 1",
    "date": "2021-10-04",
    "visitors": 40
}, {
    "page": "Page 2",
    "date": "2021-10-04",
    "visitors": 50
}, {
    "page": "Page 3",
    "date": "2021-10-04",
    "visitors": 60
}]

Every entry contains a date (date), a visitors number (visitors) and a page name (page)

For example, if a have 2 dates and 3 pages for whom i want to retrieve the numbers of visitors per date per page. My array returns 6 entries (3 pages for each date) the example above.

So how can get the the number of visits of every page and get one array containing page names & dates only once & the related visits number like:

[   {
        page: pageName,
        dates: [],
        numberOfVisits: [],
    },
    {
        page: pageName,
        dates: [],
        numberOfVisits: [],
    },
    {
        page: pageName,
        dates: [],
        numberOfVisits: []
    },
]

i tried reducing the array and filtering it according to either the dates or the page names like so:

myArray.reduce((a, c) => {
    /* Filtering by date but this returns only one page name */
    let filtered = a.filter(el => (el.date === c.date));

    /* OR Filtering by page name but this returns only oneDate for each */
    /* let filtered = a.filter(el => (el.page === c.page)); */

    if (filtered.length > 0) {
        /* summing up the number of visits => this works in both cases */
        a[a.indexOf(filtered[0])].m_unique_visitors += +c.m_unique_visitors;

    } else {
        a.push(c);
    }
})
return a;

Solution

  • I'd suggest using Array.reduce() to group dates and visitors by page.

    We'd sort the input array to ensure our output is also sorted by date.

    The accumulator (acc) will have a property for each page, with a page name, a list of dates and number of visits array.

    We'd use Object.values() to get the final array required.

    const a = [{ "page": "Page 1", "date": "2021-10-05", "visitors": 10 }, { "page": "Page 2", "date": "2021-10-05", "visitors": 20 }, { "page": "Page 3", "date": "2021-10-05", "visitors": 30 },{ "page": "Page 1", "date": "2021-10-04", "visitors": 40 }, { "page": "Page 2", "date": "2021-10-04", "visitors": 50 }, { "page": "Page 3", "date": "2021-10-04", "visitors": 60 }]
    
    const sortByDate = ({ date: a}, { date:b }) => Date.parse(a) - Date.parse(b);
    
    const result = Object.values(a.sort(sortByDate).reduce((acc, { page, date, visitors}) => { 
        acc[page] = acc[page] || { page, dates: [], numberOfVisits: []};
        acc[page].dates.push(date);
        acc[page].numberOfVisits.push(visitors);
        return acc;
    }, {}))
    
    console.log('Result:', result)
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    You could also structure the output slightly differently, to have a visits array, each element having a date and visitors value.

    const a = [{ "page": "Page 1", "date": "2021-10-05", "visitors": 10 }, { "page": "Page 2", "date": "2021-10-05", "visitors": 20 }, { "page": "Page 3", "date": "2021-10-05", "visitors": 30 },{ "page": "Page 1", "date": "2021-10-04", "visitors": 40 }, { "page": "Page 2", "date": "2021-10-04", "visitors": 50 }, { "page": "Page 3", "date": "2021-10-04", "visitors": 60 }]
       
    const sortByDate = ({ date: a}, { date:b }) => Date.parse(a) - Date.parse(b);
    
    const result = Object.values(a.sort(sortByDate).reduce((acc, { page, date, visitors}) => { 
        acc[page] = acc[page] || { page, visits: [] };
        acc[page].visits.push( { date, visitors});
        return acc;
    }, {}));
    
    console.log('Result:', result);
    .as-console-wrapper { max-height: 100% !important; top: 0; }