javascript - Farklı türlere sahip olabilen nesneler olduğunda bir anahtara göre nesne dizisi nasıl gruplanır


Benim şöyle bir nesne dizim var:

const input = [
    {
        "name" : "car",
        "sign" : "+",
        "options" : "benz"
    },
    {
        "name" : "bike",
        "sign" : "+",
        "options" : "pulsar"
    },
    {
        "name" : "bike",
        "sign" : "+",
        "options" : "enfield"
    },
    {
        "name" : "car",
        "sign" : "+",
        "options" : ["toyota","hyundai","benz"]
    },
    {
        "name" : "",
        "sign" : "",
        "options" : "" 
    },
    {
        "name" : "car",
        "sign" : "+",
        "options" : ["audi", "ford"]
    }
]

Girdi yapısı şu şekildedir: her nesnenin 3 anahtar-değer çifti vardır.

  1. Hepsi boş olabilir

  2. Geçerli değerler alabilir:

    a. Seçenekler ya bir dize olabilir

    b. Veya bir dizi olabilir

"" (boş dize) değerlerine sahip nesnelerin çıktıya eklenmesini istemiyorum, Çıktı şöyle olmalı:

[
    {"car" : [ "benz", "toyota","hyundai", "audi", "ford"]},
    {"bike" : ["pulsar","enfield" ]}
]

Diğer çeşitli cevaplar, nesne değerinin ya bir dizi ya da bir string olabileceği durumda nasıl ele alınacağını göstermiyor, bu yüzden yeni bir soru yazıyorum.

Reduce mi yoksa map mi kullanmam gerektiğinden emin değilim. Her türlü yardım çok takdir edilir, şimdiden teşekkürler.

Cevap #1

Bu oldukça standart bir 'gruplama' işlemi, bir özellik üzerinden gruplama yapar ve diğerini bir diziye toplar.

Hem dizeleri hem de dizileri karşılamak için, options değerini toplayıcıya eklemek için concat() kullanırsınız. Concat, ya bireysel değerleri ya da değer dizilerini kabul eder ve ikincisini hedef diziye düzleştirir. Çoğalmaları önlemek için bir Set kullanabilirsiniz. Aşağıdaki kod parçası, yalnızca hem name hem de options doğruysa öğeleri toplayıcıya ekler.

const input = [
  { "name": "", "sign": "+", "options": "benz" }, // blank 'name' property
  { "name": "bike", "sign": "+", "options": "" }, // blank 'options' property
  { "name": "bike", "sign": "+", "options": "enfield" },
  { "name": "car", "sign": "+", "options": ["toyota", "hyundai"] },
  { "name": "car", "sign": "", "options": "ford" },
  { "name": "car", "sign": "+", "options": ["audi", "ford"] }]

const result = Object.values(
  input.reduce((acc, { name, options }) => {
    if (name && options) {
      const category = (acc[name] || (acc[name] = { [name]: [] }));
      category[name] = [...new Set(category[name].concat(options))];
    }
    return acc;
  }, {}));

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

Alternatif olarak, her yinelemede yeni bir Set oluşturmayı önlemek için, bir Set içinde biriktirebilir ve ardından reduce() çağrısının sonucunu eşleyip her seti bir diziye dönüştürebilirsiniz.

const input = [
  { "name": "", "sign": "+", "options": "benz" }, // blank 'name' property
  { "name": "bike", "sign": "+", "options": "" }, // blank 'options' property
  { "name": "bike", "sign": "+", "options": "enfield" },
  { "name": "car", "sign": "+", "options": ["toyota", "hyundai"] },
  { "name": "car", "sign": "", "options": "ford" },
  { "name": "car", "sign": "+", "options": ["audi", "ford"] }]

const result = Object.entries(
  input.reduce((acc, { name, options }) => {
    if (name && options) {
      acc[name] || (acc[name] = new Set);
      [].concat(options).forEach(option => acc[name].add(option));
    }
    return acc;
  }, {}))
  .map(([name, options]) => ({ [name]: [...options] }));

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

Burada ES6 yineleyicilerini kullanamayacak gibi göründüğünüz için, çoğaltmaları önlemek için sadece bir Array#includes() kontrolü kullanan bir seçenek var.

const input = [
  { "name": "", "sign": "+", "options": "benz" }, // blank 'name' property
  { "name": "bike", "sign": "+", "options": "" }, // blank 'options' property
  { "name": "bike", "sign": "+", "options": "enfield" },
  { "name": "car", "sign": "+", "options": ["toyota", "hyundai"] },
  { "name": "car", "sign": "", "options": "ford" },
  { "name": "car", "sign": "+", "options": ["audi", "ford"] }]

const result = Object.values(
  input.reduce((acc, { name, options }) => {
    if (name && options) {
      const category = (acc[name] || (acc[name] = { [name]: [] }));
      [].concat(options).forEach(option => {
        if (!category[name].includes(option)) category[name].push(option)
      });
    }
    return acc;
  }, {}));

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

ES3

var input = [
  { "name": "", "sign": "+", "options": "benz" },
  { "name": "bike", "sign": "+", "options": "" },
  { "name": "bike", "sign": "+", "options": "enfield" },
  { "name": "car", "sign": "+", "options": ["toyota", "hyundai"] },
  { "name": "car", "sign": "", "options": "ford" },
  { "name": "car", "sign": "+", "options": ["audi", "ford"] }
];

var temp = {};

for (var i = 0; i < input.length; i++) {
  var name_ = input[i].name;
  var options_ = input[i].options;

  if (name_ !== undefined && name_ !== "" && options_ !== undefined && options_ !== "") {

    if (!temp.hasOwnProperty(name_)) {
      temp[name_] = {};
      temp[name_][name_] = [];
    }

    var options_ = [].concat(options_);

    for (var j = 0; j < options_.length; j++) {
      if (!temp[name_][name_].includes(options_[j])) {
        temp[name_][name_].push(options_[j]);
      }
    }

  }
}

var result = [];

for (var key_ in temp) {
  if (temp.hasOwnProperty(key_)) {
    result.push(temp[key_]);
  }
}

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

Cevap #2

İstediğiniz koşulları ekleyerek bir reduce fonksiyonu kullanabilirsiniz.

const input = [
    {
        "name": "car",
        "sign": "+",
        "options": "benz"
    },
    {
        "name": "bike",
        "sign": "+",
        "options": "pulsar"
    },
    {
        "name": "bike",
        "sign": "+",
        "options": "enfield"
    },
    {
        "name": "bike",
        "sign": "+",
        "options": "enfield"
    },
    {
        "name": "car",
        "sign": "+",
        "options": ["toyota", "hyundai"]
    },
    {
        "name": "",
        "sign": "",
        "options": ""
    },
    {
        "name": "car",
        "sign": "+",
        "options": ["audi", "ford"]
    }
]

let grouped = input.reduce(function (r, a) {
    if (a.name !== "") {
        r[a.name] = r[a.name] || [];
        if (Array.isArray(a.options)) {
            r[a.name] = Array.from( new Set (r[a.name].concat(a.options)))
        } else {
            r[a.name].push(a.options)
            r[a.name] = Array.from( new Set (r[a.name]));
        }

    }
    return r;
}, {});
let mapped = Object.entries(grouped).map(([key, value])=>{return{[key]:value}})

console.log(mapped)