In JavaScript, convert an array of objects into an array of arrays of objects, effectively grouping the objects together

How can I convert an array of objects into an array of arrays of objects? The goal is to group the objects in the original array based on specific criteria. Objects that are similar according to these criteria will be placed inside a subarray, which becomes an element of the new array.

I am struggling with inserting an initial empty array, which should not be included, and managing temporary states of subarrays along with temporary variables used as criteria.

What am I overlooking here? Is there a more concise, perhaps less procedural, and more functional approach to accomplishing this?

const transformArray = function (array) {
            let rows = [];
            let parts = [];
            let lastDay = null;
            let i = 0;
            array.forEach(function(item) {
                let currentDay = new Date(item.dt_text).getDay();
                if (currentDay !== lastDay) {
                    // Not in the same day row
                    rows.push(parts);
                    parts = [];
                    parts.push(item);

                    lastDay = currentDay;
                    i = rows.indexOf(parts);
                    return;
                } else if (currentDay === lastDay){
                    parts.push(item);
                    return;
                }
            });
            return rows;
  },

The sample data that this function handles is structured like this:

    [
      {
      "dt":1442275200,
      "main":{"temp":285.66,"temp_min":282.93,"temp_max":285.66,"pressure":899.08,"sea_level":1029.64,"grnd_level":899.08,"humidity":84,"temp_kf":2.73},
      "weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01n"}],
      "clouds":{"all":0},
      "wind":{"speed":1.18,"deg":34.0052},
      "rain":{},
      "sys":{"pod":"n"},
      "dt_text":"2015-09-15 00:00:00"
      },
      {
      "dt":1442275200,
      "main":{"temp":285.66,"temp_min":282.93,"temp_max":285.66,"pressure":899.08,"sea_level":1029.64,"grnd_level":899.08,"humidity":84,"temp_kf":2.73},
      "weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01n"}],
      "clouds":{"all":0},
      "wind":{"speed":1.18,"deg":34.0052},
      "rain":{},
      "sys":{"pod":"n"},
      "dt_text":"2015-09-15 00:00:00"
      },
      {
      "dt":1442228400,
      "main":{"temp":285.66,"temp_min":282.93,"temp_max":285.66,"pressure":899.08,"sea_level":1029.64,"grnd_level":899.08,"humidity":84,"temp_kf":2.73},
      "weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01n"}],
      "clouds":{"all":0},
      "wind":{"speed":1.18,"deg":34.0052},
      "rain":{},
      "sys":{"pod":"n"},
      "dt_text":"2015-09-14 00:00:00"
      }
    ]

Answer №1

There are two different approaches to achieving this task. The reason for including the i tag and binding this is unclear, as they are not utilized in the code. Additionally, there seems to be a missing piece where the last row of items should be pushed onto the rows array.

Firstly, let's explore a method that adheres closely to your initial syntax:

var transform = function (array) {
    var rows = []; // array to hold rows
    var parts;     // placeholder array for parts
    var lastDay;   // variable to store last day
    array.forEach(function (item) {
        var currentDay = new Date(item.dt * 1000).getDay();
        if (currentDay !== lastDay) { 
            parts = [];
            rows.push(parts);
            parts.push(item);          
            lastDay = currentDay;
        } else {
            parts.push(item);         
        }
    });
    return rows;
}

Now, let's take a look at a more optimized approach:

var transform = function (array) {
    var rows = [];
    var row;
    var lastDay;
    array.forEach(function (item) {
        var currentDay = new Date(item.dt * 1000).getDay(); 
        if (currentDay !== lastDay) { 
            row = rows.push([item]) - 1;
            lastDay = currentDay;
        } else {
            rows[row].push(item);
        }
    });
    return rows;
}

Answer №2

Your reasoning is solid.

However, you overlooked adding the row if all your data is from the same day.

To correct this, insert rows.push(parts) after the array.forEach() loop.

Additionally, here are some pointers:

  • Avoid binding array.forEach().bind(this).
  • Instead of checking for equality with empty array like parts != [], prefer checking its length using parts.length.

For a fixed version, check out this modified JSFiddle link.

Answer №3

Utilizing Array.prototype.reduce can be an effective approach, especially if the array elements are already ordered according to element.dt, as you mentioned in your question:

function transform(array) {
    var groups = [[]];

    var lastItemForLastGroup = array.reduce(function (previous, current) {
        // continue population of the latest group
        groups[groups.length - 1].push(previous);

        if (previous.dt !== current.dt) {
            // add a new group
            groups.push([]);
        }

        // return current so that it will be `previous` in the next iteration
        return current;
    });

    groups[groups.length - 1].push(lastItemForLastGroup);

    return groups;
}

Though clear for those familiar with the reduce method, it might seem complex for others, potentially leading to the impression of overusing Array.prototype.reduce.

By using the provided test data:

var harry = [
    // group 0
    {
        "dt": 1442275200,
        "main": {"temp": 285.66, "temp_min": 282.93, "temp_max": 285.66, "pressure": 899.08, "sea_level": 1029.64, "grnd_level": 899.08, "humidity": 84, "temp_kf": 2.73},
       ...
    },
    ...
];

Executing transform(harry); results in 3 groups: the first group with two items, second group with three items, and third group with two items, fitting the expected outcome.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The component name "pages/product/_slug.vue" is invalid as it does not adhere to the valid custom element name specified in HTML5

Currently, I am working with Nuxt.js and dealing with some dynamic routes. The folder structure I have set up looks like this: - pages - product - _slug.vue For linking to the route, I am using the following code: <nuxt-link :to="{ name: 'pro ...

Combining JWT authentication with access control lists: a comprehensive guide

I have successfully integrated passport with a JWT strategy, and it is functioning well. My jwt-protected routes are structured like this... app.get('/thingThatRequiresLogin/:id', passport.authenticate('jwt', { session: false }), thing ...

Directive in AngularJS fails to trigger upon form reset

I encountered an issue with a directive used within a form, specifically when clicking on a button with the type reset. The problem can be replicated on this stackblitz link. The directive I'm using is quite simple - it sets the input in an error stat ...

How to Utilize the Vue Instance With the 'this'

Trying to implement this in my VueJs methods results in an error message: this is undefined It seems like arrow functions may not be suitable as their this does not bind to the expected context. Switching to a regular function still causes the same err ...

The flow union type operates smoothly without any errors occurring

In the code snippet below, I have defined a type 'Something' which can have three possible values: 'A', 'B', or 'C'. The function 'f' takes an object of type Something as input and returns a string based on ...

D3 group of legendary elements

Is there a way to group both the circle and text elements for each legend item together? I'm currently facing a challenge with the enter/exit methods. While I can create the groups (g), I'm unable to append the text and circle elements. li.sel ...

What is the best way to achieve the effect of "overflow: hidden" in three.js or WebGL?

I'm diving into the world of designing a WebGL GUI for my games and I'm keen to explore GPU graphics in more depth, as it offers a smoother experience compared to WebKit CSS rendering. Is there a way to create a scrollview where the internal mes ...

A stylish method for converting CSV data into XML format using Node.js and Express

I am in search of a sophisticated solution to convert CSV files into hierarchical XML format based on a specific template within a Node/Express server. For example, if the CSV is of Type Template "Location": Name,Lat,Lon,Timezone name,lat,lon,timezone it ...

There are no markers or popups currently displayed on the map

Utilizing the ngx-leaflet plugin for leaflet, I have established the base layers and integrated a listener for the leafletMapReady event. Within my handler, I attempted to add both a marker and a customized popup. The handler code is displayed below: init ...

restructure array upon alteration of data

Here's the current structure of an array stored in a $scope variable: $scope.objects: [ {selected: true, description: 'string1'}, {selected: false, description: 'string2'}, {selected: true, description: 'string3'}, ...

How can I showcase a Google donut chart using an array of data in a React application?

I have created a doughnut chart that is supposed to take an array as data. However, when I input the array, nothing shows up on the chart. How can I create a chart with array data? sum.js ... const arr = []; arr.push({ key: capitalizeEachFirst ...

Searching in the Kendo Dropdown List using text and value

$(document).ready(function() { $("#selection").kendoDropDownList({ filter: "startswith", dataTextField: "SelectionName", dataValueField: "SelectionID", dataSour ...

Issue with fgetcsv() not properly handling newlines during conversion of CSV file to array

My data consists of airport codes and city names in an array with around 3500 lines. code,city "Abilene, TX ",ABI "Adak Island, AK ",ADK "Akiachak, AK ",KKI "Akiak, AK ",AKI "Akron/Canton, OH ",CAK "Akuton, AK ",KQA "Alakanuk, AK ",AUK "Alamogordo, NM ",A ...

Integrate CKEditor with elFinder to allow for direct file uploads

I am utilizing the elFinder Laravel package for file management with CKEditor. I have followed all the steps and everything is working fine except for one issue. When I click on the image button in CKEditor to select or upload an image, after selecting an ...

Is it possible to execute a function when the AJAX request is successful and returns a status code of

I am looking to implement the success function to only run a certain function if the status code is 200. I have come across this example: $.ajax ({ success: function(data,textStatus,jqXHR){ external(); } )}; However, I have not found a clear ...

Firebase web authentication is most effective upon the second attempt

I am currently working on a website that interacts with Google's firebase to read and write data. The website has both anonymous and email authentication enabled. Users can view the data anonymously, but in order to edit or write new data, they must s ...

NServicebus JavaScript message handler: Enhancing Communication

I am in the process of developing a JavaScript event subscriber for NServicebus, and I am seeking feedback on my approach as well as any potential pitfalls to watch out for in this design. My proposed components are as follows: ASP.NET MVC BusControll ...

Discover the magic of Bootstrap 3.0 Popovers and Tooltips

I'm struggling with implementing the popover and tooltip features in Bootstrap. While I have successfully implemented drop downs and modals, the tooltips are not styled or positioned correctly as shown in the Bootstrap examples, and the popover featur ...

AngularJS faces issue with view not reflecting changes made to model

A web-based game I am developing lets players bid on cards and trade them with one another. The technology stack for this application includes Node, Express, MongoDB, and Angular. The player avatars and names, along with their connection status, are displ ...

show a list of checkboxes that have been selected using JavaScript

I have a group of Checkbox options for 7 Days all with the same name of "sessionDays." I have used the following code to count the number of checked checkboxes: var totalDays = document.querySelectorAll('.sessionDays input[type="checkbox"]:checked&a ...