Most effective method for grouping array elements by multiple criteria in JavaScript using ES6 syntax

Hello developers, I have a question about how to categorize an array of objects with different values into specific sub-groups based on certain criteria. Each subgroup should contain objects with specific values according to the queried key.

Here is an example array:

const cars = 
  [ { make: 'audi', model: 'r8',      year: '2012' } 
  , { make: 'audi', model: 'rs5',     year: '2013' } 
  , { make: 'ford', model: 'mustang', year: '2012' } 
  , { make: 'ford', model: 'fusion',  year: '2015' } 
  , { make: 'kia',  model: 'optima',  year: '2012' } 
  ] 

I want to group by the key make and create a subgroup called 2nd_class for objects where the value in the make key is either kia or ford. The rest should be grouped under 1rst_class.

The expected result would look like this:

const expected = 
  [ '2nd_class': 
    [ { make: 'ford', model: 'mustang', year: '2012' } 
    , { make: 'ford', model: 'fusion',  year: '2015' } 
    , { make: 'kia',  model: 'optima',  year: '2012' } 
    ] 
  , '1rst_class' : 
    [ { make: 'audi', model: 'r8',  year: '2012' } 
    , { make: 'audi', model: 'rs5', year: '2013' } 
    ] 
  ]

I haven't been able to find examples online that address grouping by key and multiple values. Any help would be greatly appreciated!

Answer №1

If you prefer, you have the option to do the following:

const vehicles = [ 
 { make: 'audi', model: 'r8',      year: '2012' }, 
 { make: 'audi', model: 'rs5',     year: '2013' }, 
 { make: 'ford', model: 'mustang', year: '2012' },
 { make: 'ford', model: 'fusion',  year: '2015' },
 { make: 'kia',  model: 'optima',  year: '2012' } 
];

const vehicles_by_class = vehicles.reduce((accumulator,current)=>{
  const classification=(current.make==="audi"?"1st":"2nd")+"_class";
  (accumulator[classification]=accumulator[classification]||[]).push(current);
  return accumulator;}, {} );

console.log(vehicles_by_class);

The line

(accumulator[classification]=accumulator[classification]||[]).push(current);
verifies if the property accumulator[classification] already exists in the object and - if not - initializes it as an empty array before adding the current element to it.

If you want to include multiple brands under the "1st_class" category, you can modify line 2 like this:

const classification=(["audi","mercedes"].indexOf(current.make)>-1?"1st":"2nd")+ "_class";

Answer №2

If you're looking to achieve a similar outcome, consider implementing code like this:

const vehicles = [
  {
    'make': 'toyota',
    'model': 'camry',
    'year': '2019'
  }, {
    'make': 'honda',
    'model': 'accord',
    'year': '2020'
  }, {
    'make': 'chevrolet',
    'model': 'malibu',
    'year': '2017'
  }, {
    'make': 'jeep',
    'model': 'wrangler',
    'year': '2018'
  },
];

// List of used makes
const usedMakes = [];
// Result object
const formattedResult = {
  'high_end_cars': [],
  'budget_cars': []
};

// Loop through the vehicle array
vehicles.forEach(vehicle => {
  // Check if we have encountered this make before 
  if (usedMakes.indexOf(vehicle.make) === -1) {
    // Retrieve all vehicles with the same make as the current vehicle
    const filteredVehicles = vehicles.filter(v => v.make === vehicle.make);
    
    if (['chevrolet', 'honda'].includes(vehicle.make)) {
      // Add to budget cars 
      formattedResult['budget_cars'].push(...filteredVehicles)
    } else {
      // Add to high-end cars
      formattedResult['high_end_cars'].push(...filteredVehicles)
    }
    
    // Store the used make to avoid duplication
    usedMakes.push(vehicle.make);
  }
});

console.log(formattedResult)

The process of iterating, checking for unused values, processing accordingly, and storing to prevent reuse is a fundamental programming algorithm.

Answer №3

Here is a way to achieve this:

const cars = 
  [ { make: 'audi', model: 'r8',      year: '2012' } 
  , { make: 'audi', model: 'rs5',     year: '2013' } 
  , { make: 'ford', model: 'mustang', year: '2012' } 
  , { make: 'ford', model: 'fusion',  year: '2015' } 
  , { make: 'kia',  model: 'optima',  year: '2012' } 
  ] 

const Class2 = [ 'ford', 'kia' ]

const expected = cars.reduce( (r,c) =>
  {
  let cls = Class2.includes(c.make) ? '2nd_class':'1rst_class'
  r[cls].push({...c})
  return r
  } , {'2nd_class':[],'1rst_class':[] }  ) 

console.log( expected )
.as-console-wrapper {max-height: 100%!important;top:0 }

Answer №4

Instead of crafting a one-time solution tailored to your specific scenario, I took the initiative to develop a versatile grouping function. This particular type of grouping goes beyond the conventional approach utilities use to group items, requiring more detailed input.

Introducing the function:

groupBy(arr, propToGroup, mapping)

This function expects an array of objects to be grouped, the property within those objects to analyze for grouping, and a mapping object that specifies which values for that property should belong to each group.

Below is a runnable version you can test in this snippet:

function groupBy(arr, propToGroup, mapping, defaultMapping) {
    let output = new Map();
    for (let item of arr) {
        // get value of our property of interest
        let val = item[propToGroup];
        if (val === undefined) {
            if (defaultMapping) {
                val = defaultMapping;
            } else {
                throw new Error(`No value for property .${propToGroup} and no defaultMapping`);
            }
        }
        let classification = mapping.get(val);
        if (!classification) {
            if (!defaultMapping) {
                throw new Error(`Property value ${val} is not present in mapping and no defaultMapping`);
            }
            classification = defaultMapping;
        }
        let classificationArray = output.get(classification);
        // if classification not found yet, then initialize as empty array
        if (!classificationArray) {
            classificationArray = [];
            output.set(classification, classificationArray);
        }
        classificationArray.push(item);
    }
    // convert to output format
    let result = [];
    for (let [key, val] of output) {
        result.push({
            [key]: val
        });
    }
    return result;
}

const cars = [
    { make: 'audi', model: 'r8', year: '2012' },
    { make: 'audi', model: 'rs5', year: '2013' },
    { make: 'ford', model: 'mustang', year: '2012' },
    { make: 'ford', model: 'fusion', year: '2015' },
    { make: 'kia', model: 'optima', year: '2012' },
    { make: 'vw', model: 'bug', year: '1960' },
];

const mapping = new Map([
    ['audi', '1rst_class'],
    ['ford', '2nd_class'],
    ['kia', '2nd_class']
]);

let result = groupBy(cars, "make", mapping, "other");
console.log(result);

The aim is to have the flexibility to utilize this groupBy() function in diverse scenarios. If a property value isn't found in the mapping but a defaultMapping is provided, it will be sorted into the defaultMapping category. Without a defaultMapping, it will raise an exception if not in the mapping.

Note that implementing a defaultMapping increases the code length, but it helps handle unexpected data or situations where you want a "catchall" group to hold everything not explicitly defined in the mapping. While unnecessary for your current issue, it enhances the general usability of the function across various contexts.


Explanation of Function:

  1. Create a Map object internally to track encountered groups, with group names as keys and arrays of objects within each group as corresponding values.

  2. Iterate through the array of objects.

  3. Retrieve the property value of interest from the object.

  4. If the property doesn't exist, attempt to use the default mapping.

  5. If the property exists, find its classification in the mapping.

  6. If no classification is found, try using the defaultMapping.

  7. Check for the classification in our temporary output Map.

  8. If not found, create an empty array for this classification.

  9. Add the item to the classification array.

  10. Upon finishing iteration, convert the internal Map object to the desired array structure and return it.

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

Are there any implementations of RFC-6570 for URI templates using JavaScript?

When it comes to registering a controller in node and express, I typically use the following syntax: app.get('/user/:id', function (req, res) {...}); But now, I'm interested in doing it the rfc-6570 way: app.get('/user/{id}', func ...

There is a bug that I have encountered specifically on RN version 0.74

Everything was running smoothly in RN 0.73.7, but encountered an issue on Android when upgrading to RN 74 with the following error message: Error: Failed to create a new MMKV instance: React Native is not running on-device. MMKV can only be used when syn ...

Trouble arises when attempting to use JavaScript, JSP, and JSON in conjunction with

I am in the process of creating a client-server application where I send a request from the client to the server using a JSON object for registration. However, despite receiving a JSON response with an "OK" field as expected, the client keeps triggering th ...

I am attempting to utilize diagram.highlightCollection in gojs to showcase nodes that do not meet a specific condition

I have a list of node IDs (keys) and I need to highlight those nodes. For empty nodes, the same condition works fine. For example: checkEmptyNodes() { const emptyNodes = []; const diagDetails = this.myserv.getDiagramData(); ...

Dealing with errors when implementing an Angular 2 route guard that returns an Observable of type boolean can be a

My route guard is implemented as follows: @Injectable() export class AuthGuard implements CanActivate { constructor(private router: Router, private authenticationSvc: AuthenticationService) { } canActivate(): Observable<boolean> { return this. ...

The child component's template content fails to display properly within the parent component hosting it

Currently, I am utilizing Vue3 with options api in the code snippet below. Within my parent component, I have embedded a child component as demonstrated. The issue arises during runtime where even though showNotificationsWindowsContainer is set to true, t ...

Using JavaScript to we can transfer the function result to an HTML input field

Is there a way to display the result of a JavaScript function in an HTML input field? Here is an example form: https://i.sstatic.net/HHHl2.png When I click "Run - Script," the following simple script is executed: <button type="submit" class="btn btn ...

Utilizing NPM configuration variables across different operating systems (Windows and Linux): A comprehensive guide

When you use the syntax npm_package_config_<variable>, its usage varies depending on the operating system. In package.json (for Linux & Windows) config: { foo: "bar" } Then for usage: On Linux node app.js --arg=$npm_package_config_foo On Wi ...

Searching for images in the filesystem using a recursive deferred approach

I've been attempting to iterate through the Android file system in search of any images. I created the following recursive method, but it's not functioning as expected. I've been struggling to determine a condition that can identify the end ...

How to showcase an item in Angular 2

Alright, I have the following data: { "calledCust": { "no": 270, "yes": 9 }, "hasOpened": { "no": 254, "yes": 25 } } I've been attempting to display this in the view using the code below. <ul *ngFor="let profile of profiles.counts"> <li& ...

Load an XML file from the local server asynchronously on the Chrome web browser

Attempting to load a local XML/XSL file into a variable for editing seems to be causing an issue. The code provided functions properly in IE and Chrome, however, Chrome displays a warning due to the synchronous nature of the call. function loadXMLDoc(fileN ...

"Exploring the world of JavaScript, Ajax, PHP parser errors, and the process of obtaining post data

Having an issue with the AJAX functionality in my game.php file. Despite my efforts to refresh .refre, I keep encountering a "parsererror" message. The jsonString variable is created using JSON.stringify(object). <div class="refre"></div> < ...

Notifying users with a jQuery or Javascript alert when they click the back

I am trying to create an alert or redirection for users when they press the back button, but so far, all the solutions I have attempted only trigger the alert after a form is submitted. Here is the current ineffective code I am using: $(function() { i ...

Struggling with the functionality of having numerous jQuery UI controls on a single page. Implementing the accordion control in multiple sections

For my current project, I am utilizing the jQuery UI accordion control twice on a single page. The first one functions perfectly fine, but when I attempt to add a second accordion, the original one stops working while the new one works correctly. Does any ...

The function addView is not recognized by Framework7

I am embarking on the journey of creating a Cordova app with Framework7 as my UI framework. My goal is to adopt inline pages as the architectural layout, but I am encountering an error in the console during project setup: An error message of "Uncaught Typ ...

What methods can I use to ensure precision in my raycasting while maneuvering the mouse over various sections of a LineSegment?

I have created a demonstration for drawing a LineSegment using three.js. I have implemented vertexColors in the material and assigned colors to the vertices. When hovering over different parts of the LineSegment, I change the color of the selected vertex ( ...

The AngularJS error message: TypeError: Unable to access the 'images' property because it is undefined

Currently using AngularJS version 1.5.11 I am attempting to present images sourced from a JSON array. Is my method correct: $scope.mainImage = $scope.template.images[0].name;. The issue arises at the line where it says it cannot read property of images. ...

The echo statement is failing to show any value following the ajax request

I am in need of assistance. I would like to output the value from a PHP echo statement using an AJAX call. Currently, the code is functioning without any errors. When I select options from a dropdown menu, the value is displayed on the console by using con ...

Is there a way to display a modal newsletter popup just one time during a user's session?

I am trying to implement a modal popup that only shows once per session, but I am facing an issue where the div pops up every time I visit the homepage. My attempt to use cookies to fix this problem has not been successful. <script> if (!Cooki ...

Error: The method 'editable' is not defined for the jQuery.jEditable object [object Object]

It seems like there is an error with this jeditable object. This is my webpage <script src="/assets/jquery.js?body=1" type="text/javascript"></script> <script src="/assets/jquery_ujs.js?body=1" type="text/javascript"></script> .. ...