3

I feel embarrassed for asking this question as I should know how to figure it out, but I'm spinning my wheels on grouping an array of objects by multiple keys.

Here's the data:

[
      {
        "car": "audi",
        "type": "A6",
        "style": "Avant",
        "year": "1996"
      },
      {

        "car": "audi",
        "type": "A4",
        "style": "2",
        "year": "2006"
      },
      {

        "car": "audi",
        "type": "A4",
        "style": "L W12",
        "year": "2006"
      },
      {

        "car": "audi",
        "type": "80",
        "style": "GLE",
        "year": "1975"
      },
      {

        "car": "audi",
        "type": "A6",
        "style": "Avant L",
        "year": "1996"
      },
      {
        "car": "audi",
        "type": "A6",
        "style": "3.2 Multitronic",
        "year": "2006"
      },
]

What I've been trying to generate with little success is the following:

 [{
   "audi": [{
     "1996": {
       "A6": ["Avant, Avant L"]
     }
   }, {
     "2006": }
       "A6": ["3.2 Multitronic"],
       "A4": ["L W12", "2"]
     }
   }
   ....
 }]

The schema is:

{
    "car1": [{
        "year1": {
            "style1": ["trim1", "trim2"],
            "style2": ["trim1", "trim2"]
        },
        "year1": {
            "style1": ["trim1", "trim2"],
            "style2": ["trim1", "trim2"]
        }
    }],
    "car2": [{
        "year1": {
            "style1": ["trim1", "trim2"],
            "style2": ["trim1", "trim2"]
        },
        "year2": {
            "style1": ["trim1", "trim2"],
            "style2": ["trim1", "trim2"]
        }
    }]
}

I've tried the following with lodash

  let result = _.chain(carData)
    .groupBy('car')
    .toPairs()
    .map(function(curr) {
        return _.zipObject(['car', 'year'], curr);
    })
    .value();

This gets me part of the way, but I end up with incomplete data when it comes to the styles and types for each year of the car.

2
  • 3
    please add your attempt. Commented Feb 24, 2017 at 21:48
  • Updated, but I'm trying not to use lodash in this case Commented Feb 24, 2017 at 22:00

3 Answers 3

2

You could use a hash object and a nested approach for the given properties.

var data = [{ car: "audi", type: "A6", style: "Avant", year: 1996 }, { car: "audi", type: "A4", style: 2, year: 2006 }, { car: "audi", type: "A4", style: "L W12", year: 2006 }, { car: "audi", type: 80, style: "GLE", year: 1975 }, { car: "audi", type: "A6", style: "Avant L", year: 1996 }, { car: "audi", type: "A6", style: "3.2 Multitronic", year: 2006 }],
    keys = ['car', 'year', 'type'],
    result = [];

data.forEach(function (a) {
    keys.reduce(function (r, k) {
        var o = {};
        if (!r[a[k]]) {
            r[a[k]] = { _: [] };
            o[a[k]] = r[a[k]]._;
            r._.push(o);
        }
        return r[a[k]];
    }, this)._.push(a.style);
}, { _: result });
   
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

2 Comments

My mind is blown. Would you mind stepping through your code a bit? I'm especially curious about the { _: result } being passed in as the initial value for reduce().
actually this method is basically as similar as Convert an array of json objects in to another json object, where just the keys are easier to access and the result is easier created without callback. for the hash table, you get an insight of the methods and the grouping capabilities.
1

Here's a (slightly verbose) solution that generates exactly the JSON object shape you wanted and groups by unlimited keys:

var cars = [{
  "car": "audi",
  "type": "A6",
  "style": "Avant",
  "year": "1996"
}, {

  "car": "audi",
  "type": "A4",
  "style": "2",
  "year": "2006"
}, {

  "car": "audi",
  "type": "A4",
  "style": "L W12",
  "year": "2006"
}, {

  "car": "audi",
  "type": "80",
  "style": "GLE",
  "year": "1975"
}, {

  "car": "audi",
  "type": "A6",
  "style": "Avant L",
  "year": "1996"
}, {
  "car": "audi",
  "type": "A6",
  "style": "3.2 Multitronic",
  "year": "2006"
}, ];

function groupBy(list, prop) {
  return list.reduce((groupped, item) => {
    var key = item[prop];
    delete item[prop];
    if (groupped.hasOwnProperty(key)) {
      groupped[key].push(item);
    } else {
      groupped[key] = [item];
    }
    return groupped
  }, {});
}

function groupSubKeys(obj, properties, propIndex) {
  var grouppedObj = groupBy(obj, properties[propIndex]);
  Object.keys(grouppedObj).forEach((key) => {
    if (propIndex < properties.length - 2) {
      grouppedObj[key] = groupSubKeys(grouppedObj[key], properties, propIndex + 1);
    } else {
      grouppedObj[key] = grouppedObj[key].map(item => item[properties[propIndex + 1]])
    }
  });
  return grouppedObj;
}

function groupByProperties(list, properties) {
  return groupSubKeys(list, properties, 0);
}

console.log(groupByProperties(cars, ['car', 'year', 'type', 'style']));

Here's a running example: http://codepen.io/rarmatei/pen/evmBOo

1 Comment

This one looks great too. I couldn't get my head wrapped around grouping by subKeys and would end up with empty arrays. I appreciate the time you provided to put this together.
1

const groupBy = function groupBy(list, properties, propertyIndex)  {
      // current property index
      let i = propertyIndex === undefined ? 0 : propertyIndex;
    
      // group by
      let grouppedObj = list.reduce((acc, obj) => {
        let groupedValue = obj[properties[i]];
        if (!groupedValue) {
          return acc;
        }
        if (!acc[groupedValue]) {
          acc[groupedValue] = [];
        }
        acc[groupedValue].push({ ...obj, groupBy: properties.join(",") });
        return acc;
      }, {});
    
      // group by nested
      const keys = Object.keys(grouppedObj);
      if (i === properties.length - 1) {
        return grouppedObj;
      }
      keys.forEach((key) => {
        grouppedObj[key] = groupBy(grouppedObj[key], properties, i + 1);
      });
      return grouppedObj;
    };
    
    const data =[
  {
    "year": "2021",
    "cabin": "1",
    "months": ["1", "2"]
  },
  {
    "year": "2021",
    "cabin": "1",
    "months": ["4"]
  },
  {
    "year": "2021",
    "cabin": "2",
    "months": ["1", "2"]
  },
  {
    "year": "2022",
    "cabin": "1",
    "months": ["1", "2"]
  },
  {
    "year": "2022",
    "cabin": "1",
    "months": ["4"]
  },
  {
    "year": "2022",
    "cabin": "2",
    "months": ["1", "2"]
  }
];

 const results=groupBy(data, ["year", "cabin"]);
console.log(results);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.