简体   繁体   中英

Group array objects so that objects with similar key are not put into the same group

I have the following array called 'cars'.I want to group data so that objects with similar key are not put into the group(array).

var 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'
},
];

How do i group the data so that array objects with same make are not put into the same group.The result would look like this.

var cars = {
'class_1': [
    {   'make' : 'audi',
        'model': 'r8',
        'year': '2012'

    },{ 'make' : 'ford',
        'model': 'mustang',
        'year': '2012'
    },{
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
      }
],


'class_2': [
    {
        'make' : 'audi',
        'model': 'rs5',
        'year': '2013'
    },{
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }
]
}

You can use Array.reduce() with a helper array to group the items. The helper array holds the makes that are in each group. For each object, you Array.findIndex() to find a group that doesn't include the make . If none exists, and a new group, and update the helper .

 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 helper = []; const result = cars.reduce((r, o) => { let i = helper.findIndex((g) => !g[o.make]); if(i === -1) { i = helper.push({ [o.make]: true }); r[`class_${i}`] = [o]; } else { r[`class_${i + 1}`].push(o); helper[i][o.make] = true; } return r; }, Object.create(null)); console.log(result); 

First, you should group the cars into an object (the key will be the make of the car and the value will be the array of cars with that make ). Then find the length of the biggest array among those grouped arrays (which will be the number of the classes). Then make the classes by taking (if exists) a car from each make array:

function groupCars(cars) {
    // 1. group into an object
    var group = cars.reduce(function(acc, car) {                   // for each car in cars
        if(acc.hasOwnProperty(car.make)) {                         // if there is already an a sub array for this current car's make
            acc[car.make].push(car);                               // push it to that array
        } else {
            acc[car.make] = [car];                                 // if not, create a new sub array that initially contains this car
        }
        return acc;
    }, {});

    // 2. find the biggest array
    var makes = Object.keys(group);                                // get an array of all the makes (["audi", ...]) which are the keys of the object group
    var len = Math.max.apply(null, makes.map(function(make) { return group[make].length; })); // map each make into the length of its array of cars and choose the maximum of those length as len

    // 3. make the classes, picking up a car from each array (if it exists)
    var res = {};                                                  // our result object
    for(var i = 0; i < len; i++) {                                 // for...
        var cur = res["class_" + (i + 1)] = [];                    // make a new class array
        makes.forEach(function(make) {                             // for each make in makes
            if(i < group[make].length) {                           // if the current make's array is not empty yet
                cur.push(group[make][i]);                          // take the car at the current index and push it to the current class
            }
        });
    }
    return res;
}

which can be made shorter using ES6 arrow functions:

function groupCars(cars) {
    // 1. group into an object
    let group = cars.reduce((acc, car) => ((acc.hasOwnProperty(car.make)? acc[car.make].push(car): acc[car.make] = [car]), acc), {});

    // 2. find the biggest array
    let makes = Object.keys(group);
    let len = Math.max.apply(null, makes.map((make) => group[make].length));

    // 3. make the classes, picking up a car from each array (if it exists)
    let res = {};
    for(let i = 0; i < len; i++) {
        var cur = res["class_" + (i + 1)] = [];
        makes.forEach((make) => i < group[make].length && cur.push(group[make][i]);
    }
    return res;
}

Example:

 function groupCars(cars) { let group = cars.reduce((acc, car) => ((acc.hasOwnProperty(car.make)? acc[car.make].push(car): acc[car.make] = [car]), acc), {}); let makes = Object.keys(group); let len = Math.max.apply(null, makes.map((make) => group[make].length)); let res = {}; for(let i = 0; i < len; i++) { var cur = res["class_" + (i + 1)] = []; makes.forEach((make) => i < group[make].length && cur.push(group[make][i]); } return res; } var arr = [{"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"}]; console.log(groupCars(arr)); 

You can do it like this.

// helper function to add single car to single class if possible
function addCarToClass(car, cls) {
  // it the class already contains car with that key - fail
  for (let i = 0; i < cls.length; i++) {
    if (cls[i].make === car.make) return false;
  }
  // else push it to that class
  cls.push(car);
  return true;
}

// hepler function to find class to which the car should be placed
function findClassForCar(car, classes) {
  let placed = false;

  for (let i = 0; i < classes.length; i++) {
    if (addCarToClass(car, classes[i])) {
      placed = true;
      break;
    };
  }

  // if the car wasn't placed, start a new class and place it there
  if (!placed) {
    let cls = [];
    cls.push(car);
    classes.push(cls);
  }
}

// final function to split cars accordingly
function splitToClasses(cars) {
  const classes = [];

  for (let i = 0; i < cars.length; i++) {
    findClassForCar(cars[i], classes);
  }

  // format result according to your question
  const res = {};
  for (let i = 0; i < classes.length; i++) {
    res['class_' + i] = classes[i];
  }
  return res;
}

console.log(splitToClasses(cars));

This is not likely to be efficient, so if you're dealing with hundreds of thousands or more cars in your list, you might try to find something else. But the code is pretty simple:

 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 separateMakes = cars => cars.reduce((groupings, car) => { const groups = Object.values(groupings) let group = groups.find(group => group.every(test => test.make != car.make)) || (groupings[`classes_${groups.length + 1}`] = []) group.push(car) return groupings }, {}) console.log(separateMakes(cars)) 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM