Arrange a collection of objects by two criteria, grouping them together and generating a new array with distinct keys for each group

I am facing a challenge with the following array:

const items = [
  { name: 'john', class: 'one', section: 'a' },
  { name: 'john2', class: 'one', section: 'b' },
  { name: 'john3', class: 'one', section: 'a' },
  { name: 'john4', class: 'two', section: 'a' },
  { name: 'john5', class: 'two', section: 'b' },
  { name: 'john6', class: 'two', section: 'b' },
  { name: 'john7', class: 'three', section: 'a' },
  { name: 'john8', class: 'four', section: 'a' },
  { name: 'john9', class: 'four', section: 'b' }
];

I want to group it in a specific manner, like this:

[
    {
        'oneA': [
            { name: 'john', class: 'one', section: 'a' },
            { name: 'john3', class: 'one', section: 'a' }
        ]
    },
    {
        'oneB': [
            { name: 'john2', class: 'one', section: 'b' }
        ]
    },
    {
        'twoA': [
            { name: 'john4', class: 'two', section: 'a' }
        ]
    },
    {
        'twoB': [
            { name: 'john5', class: 'two', section: 'b' },
            { name: 'john6', class: 'two', section: 'b' }
        ]
    },
    {
        'threeA': [
            { name: 'john7', class: 'three', section: 'a' }
        ]
    },
    {
        'fourA': [
            { name: 'john8', class: 'four', section: 'a' }
        ]
    },
    {
        'fourB': [
            { name: 'john9', class: 'four', section: 'b' }
        ]
    }
]

I have attempted the following solution:

items.sort(function (a, b) {
  if (a.class > b.class) return 1;
  if (a.class < b.class) return -1;

  if (a.section > b.section) return 1;
  if (a.section < b.section) return -1;
})

While this code successfully orders the array as intended, it does not achieve the desired grouping. Is there an alternative method that can accomplish this?

Answer №1

If you want to organize them together, one approach is to use a Map.

The process involves creating a key based on the values of class and converting section to uppercase. If the key exists in the map, it retrieves the corresponding value; otherwise, it initializes an empty array to store the object. This array is then set as the new value in the map.

Array.from extracts all key/value pairs from the map and constructs new objects using a computed property name.

const
    getKey = o => `${o.class}${o.section.toUpperCase()}`,
    items = [{ name: 'john', class: 'one', section: 'a' }, { name: 'john2', class: 'one', section: 'b' }, { name: 'john3', class: 'one', section: 'a' }, { name: 'john4', class: 'two', section: 'a' }, { name: 'john5', class: 'two', section: 'b' }, { name: 'john6', class: 'two', section: 'b' }, { name: 'john7', class: 'three', section: 'a' }, { name: 'john8', class: 'four', section: 'a' }, { name: 'john9', class: 'four', section: 'b' }],
    result = Array.from(
        items.reduce(
            (m, o) => m.set(getKey(o), [...(m.get(getKey(o)) || []), o]),
            new Map
        ),
        ([k, v]) => ({ [k]: v })
    );

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

Answer №2

const data = [
    { name: 'john', class: 'one', section: 'a' },
    { name: 'john2', class: 'one', section: 'b' },
    { name: 'john3', class: 'one', section: 'a' },
    { name: 'john4', class: 'two', section: 'a' },
    { name: 'john5', class: 'two', section: 'b' },
    { name: 'john6', class: 'two', section: 'b' },
    { name: 'john7', class: 'three', section: 'a' },
    { name: 'john8', class: 'four', section: 'a' },
    { name: 'john9', class: 'four', section: 'b' }
];

let groupedData = data.reduce((result, item) => {
    let index = result.findIndex(obj => Object.keys(obj).includes(item.class + item.section))
    if (index == -1) result.push({ [item.class + item.section]: [item] })
    else result[index][item.class + item.section].push(item)
    return result
}, [])
console.log(groupedData)

Answer №3

If you're looking to group items in an array based on certain criteria, you can leverage the power of the reduce function like this:

const items = [
  { name: 'john', class: 'one', section: 'a' },
  { name: 'john2', class: 'one', section: 'b' },
  { name: 'john3', class: 'one', section: 'a' },
  { name: 'john4', class: 'two', section: 'a' },
  { name: 'john5', class: 'two', section: 'b' },
  { name: 'john6', class: 'two', section: 'b' },
  { name: 'john7', class: 'three', section: 'a' },
  { name: 'john8', class: 'four', section: 'a' },
  { name: 'john9', class: 'four', section: 'b' }
];

const groupedItems = items.reduce((result, current) => {
  const key = current.class + current.section.toUpperCase();
  
  if (Array.isArray(result[key])) {
    result[key].push(current);
  } else {
    result[key] = [current];
  }
  
  return result;
}, {});

const expectedResult = Array.from(Object.keys(groupedItems), (elem) => ({[elem]: groupedItems[elem]}));

console.log(expectedResult);

Answer №4

If you're aiming to generate a collection of objects, it may be best to remove the outer array from your output.

To resolve this issue, simply cycle through each item and verify if the key required is already present in your result object. If not, create it, and then add the current item to the appropriate array.

const items = [
  { name: 'john', class: 'one', section: 'a' },
  { name: 'john2', class: 'one', section: 'b' },
  { name: 'john3', class: 'one', section: 'a' },
  { name: 'john4', class: 'two', section: 'a' },
  { name: 'john5', class: 'two', section: 'b' },
  { name: 'john6', class: 'two', section: 'b' },
  { name: 'john7', class: 'three', section: 'a' },
  { name: 'john8', class: 'four', section: 'a' },
  { name: 'john9', class: 'four', section: 'b' }
];

let result = {};

items.forEach(item => {
  let key = `${item.class}${item.section.toUpperCase()}`;
  if(!result.hasOwnProperty(key)) {
    result[key] = [];
  }
  result[key].push(item);
});

console.info(result);

Answer №5

If you're looking to understand how to achieve the desired outcome in vanilla JavaScript without relying on built-in functions, consider the following script:

var items = [
    { name: 'john', class: 'one', section: 'a' },
    { name: 'john2', class: 'one', section: 'b' },
    { name: 'john3', class: 'one', section: 'a' },
    { name: 'john4', class: 'two', section: 'a' },
    { name: 'john5', class: 'two', section: 'b' },
    { name: 'john6', class: 'two', section: 'b' },
    { name: 'john7', class: 'three', section: 'a' },
    { name: 'john8', class: 'four', section: 'a' },
    { name: 'john9', class: 'four', section: 'b' }
];
var resultArray =[];
for(var i=0;i<items.length;i++){
    if(resultArray[items[i]['class']+items[i]['section'].toUpperCase()]){
        resultArray[items[i]['class']+items[i]['section'].toUpperCase()].push(items[i]);
    } else {
        var a ="";
        a = items[i]['class']+items[i]['section'].toUpperCase();
        resultArray[a]=[];
        resultArray[a].push(items[i])
    }
}

console.log(resultArray);

https://i.sstatic.net/Cw6N1.png

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

Adjust the size of an event in the Angular Full Calendar with Chronofy, while utilizing constraints to control the drag and drop functionality

I'm currently in the process of developing an availability calendar for scheduling meetings during open times. If a time slot is unavailable, users should not be able to place an event there. While I have successfully implemented this feature, I am ...

Unable to view Chart.js on the second tab

I'm currently working on two different charts for a project - a bar chart and a line chart. The bar chart is displayed on the first tab, while the line chart is on the second tab. Interestingly, the bar chart functions properly, and when I point the l ...

The jQuery UI datepicker struggles to show the entire year in its year selection menu

Recently, I developed a function within an MVC Struts application's JSPs that sets up a generic jQuery-UI datepicker. Here is the code: function setUpGenericDate(id) { var d=new Date(); $("#" + id).datepicker({ duration: 'fa ...

Using React Native with Redux: Filtering Data and Preserving Initial Data in Redux State

For my mobile app, I decided to add a search feature using React-Native & Redux. I fetch data from an API and store it in the redux store so that I have a list of items readily available when the app is running. Now, with a searchbox in place, I want to f ...

Turn off integrity verification for local dependencies in package-lock.json

Is there a way to bypass the integrity check for a local dependency in package-lock.json? Within my project repository, I have a core library along with two Angular applications that both rely on this core library as a dependency. The problem arises beca ...

Exploring Selenium: Clicking on Auto-Complete Suggestions using Python

Attempting to interact with an auto-complete search bar on the site in order to search for results. Wanting to click on the drop-down element that appears after entering a city name to perform a full city name search and obtain results. Below is the cod ...

Customize the label and value in Material UI React Autocomplete

If you visit this link, you can see an example of what I'm trying to achieve. My goal is to have the option label and value be different from each other. In the provided example, the following code snippet is used: const defaultProps = { ...

Generating a list containing sub-items extracted from a JSON structure

I have created a list using the code provided in this fiddle. <ul></ul> var myList = [{ "title": "Home", "sub": 0, "url": "/home", "show": 1 }, { "title": "News", "sub": 0, ...

Getting just the outer edges of intricate BufferGeometry in Three.js

Currently, I am immersed in a project that involves zone creation and collision detection using Three.js. The primary objective is for my application to effectively manage collisions and produce a BufferGeometry as the final output. My aim is to visually r ...

combine and refresh identical items within an array

Currently, I am in the process of creating a prototype for an item-list and a shopping-cart. Both components function as standalone entities but are connected through a vuex-store. The item-list contains various elements that can be added to the shopping-c ...

Incorporate v-if to target a particular item within a v-for loop

On my Vue page, I have the following HTML code snippet: <div v-for="profile in lab.profiles" v-if="edit || profile.active" class="lab-tests-row-div" @mouseover=""> <div class="clickBox" :class="['clickBox-' + lab.id + ' ...

What is the proper way to integrate three.js (a third-party library) into the view controller of an SAPUI5 application

Seeking a Solution Is there a way to integrate the three.js library into SAPUI5 in order to access it using THREE as the root variable in my main view controller? I attempted to create a directory named libs within my project folder and include it in the ...

Transforming an element into a buffer using JavaScript

Utilizing Plain JavaScript for Data Transfer in Web Workers In my current project, I am avoiding the use of Node and sticking to plain JavaScript within the browser. One challenge I encountered is efficiently sending data to web workers. After some experi ...

Unable to perform a GET request to an API with custom headers

I am attempting to send a GET request to an API, but I am encountering an error: Failed to fetch. What could be causing this issue? const getData = () => { fetch("https://test-docs.stores.kg/api/categories", { method: "GET", he ...

The slow rendering of Threejs is causing the browser to become unresponsive

Utilizing ThreeJS, I successfully created a captivating 3D scene with ten thousand particles arranged in a specific layout. Rendering these particles in the 3D world was seamless thanks to ThreeJS. However, I encountered an issue where the browser would di ...

The Jquery function was implemented twice in the code

I am working on a code snippet that creates progress bars for checkboxes. My goal is to have multiple progress bars on the same page. How can I trigger the function uniquely for each div? $(window).load(function(){ $(function() { $("#reminder").progre ...

Preload-webpack-plugin does not support pre-fetching files

I have a query about prefetching and preloading content. In my vue app, I noticed that after building, I have duplicate files loaded in my dist/index.html file. Here is an example: Additionally, the "scripts" are not being preloaded/prefetched as expec ...

[karma]: Oops! We've encountered a snag - the module [expose-loader?jQuery!jquery] couldn't be

Currently, I am working on setting up karma tests for typescript. karma.conf.js module.exports = function (config) { config.set({ frameworks: ['jasmine', 'karma-typescript'], files: [ {pattern: 'sr ...

``There seems to be an issue with the toLocaleString function not properly formatting the

I have been tasked with writing a function that follows specific instructions, but for some reason, it is not working as expected. Here are the guidelines: Utilize the .toLocaleString function on both the amount and buyerCountry to format the amount into ...

All static functions in Selenium Webdriver can be accessed through a single line

var findById = driver.findElement(By.id("id")) var findByClass = driver.findElement(By.className("class")) var findByXpath = driver.findElement(By.xpath("xpath")) Is there a way to simplify the above code into one line like this: var dynamicLocator = "id ...