2
votes

Regrouper par plusieurs clés dans le tableau d'objets javascript

J'ai un tableau imbriqué d'objets qui ressemble à quelque chose comme ci-dessous.

    const result = Object.values(data.reduce((key, curr) => {
        const { region, country, t2, t3, t4, t5, t6, t7 } = curr;
        if (!key[country]) {
            let obj = {};
            obj.region = region;
            obj.country = country;
            obj.t2 = [{
                id: t2,
                t3: [{
                    id: t3,
                    t4: {
                        id: t4,
                        t5: t5
                    }
                }]
            }];
            key[country] = obj;
        } else {
            key[country].t2 = key[country].t2 || [];
            const foundCountry = key[country].t2.find(x => x.desc === t2);
            if (!foundCountry) {
                key[country].t2.push({
                    id: t2,
                    t3: [{
                        id: t3,
                        t4: {
                            id: t4,
                            t5: t5
                        }
                    }]
                });
            } else {
                const tx = foundCountry.find(x => x.id === t3);
                if (!tx) {
                    foundCountry.push({
                        id: t3,
                        t4: {
                            id: t4,
                            t5: t5
                        }
                    });
                } else {
                    tx.id = t3;
                    tx.t4 = t4;
                }
            }
        }
        return key;
    }, {}));
    console.log(util.inspect(result, false, null, true))
    return result;

Je cherche à regrouper l'ensemble de mon objet de données sur la base de combinaisons uniques de région, de pays, de t2-t7 et d'avoir une sortie comme celle-ci

[{
  "region": "Africa",
  "country": [{
      "desc": "Madagascar",
      "t2": [{
        "id": "AFR",
        "localLanguageName": "Malagasy, French",
        "localLanguageCode": "MLG, FRE"
        "t3": [{
          "id": "MG"
        }]
      }]
    },
    {
      "desc": "Morocco (incl. Western Sahara)",
      "subTerritory": [{
        "t2": "AFR",
        "t3": [{
          "id": "MA",
          "localLanguageName": "Arabic, French",
          "localLanguageCode": "ARA, FRE"
          "t4": [{
              "id": "MAXEH",
              "localLanguageName": "Arabic, French",
              "localLanguageCode": "ARA, FRE"
              "t5": [{
                "id": ""
                  .
                  .
                  .
              }]
            },
            {
              "id": "EH",
              "localLanguageName": "Arabic, French",
              "localLanguageCode": "ARA, FRE"
              "t5": [{
                "id": ""
                  .
                  .
                  .
              }]
            }]
        }]
      }]
    }]
}]

Je cherche le moyen le plus efficace de regrouper les données. Vaut-il mieux utiliser un hashmap? ou les méthodes map / reduction en Javascript?

J'ai essayé ci-dessous. C'est évidemment incomplet, mais je suis bloqué après quelques itérations.

[
    {
        "region": null,
        "country": null,
        "territory": "Worldwide",
        "territoryCode": "ALL",
        "t2": null,
        "t3": null,
        "t4": null,
        "t5": null,
        "t6": null,
        "t7": null,
        "localLanguage": {
            "territoryId": 1,
            "localLanguageName": "N/A",
            "localLanguageCode": null
        }
    },
    {
        "region": "Africa",
        "country": "Madagascar",
        "territory": null,
        "territoryCode": "MG",
        "t2": "AFR",
        "t3": "MG",
        "t4": null,
        "t5": null,
        "t6": null,
        "t7": null,
        "localLanguage": {
            "territoryId": 30,
            "localLanguageName": "Malagasy, French",
            "localLanguageCode": "MLG, FRE"
        }
    },
    {
        "region": "Africa",
        "country": null,
        "territory": null,
        "territoryCode": "AFR",
        "t2": "AFR",
        "t3": null,
        "t4": null,
        "t5": null,
        "t6": null,
        "t7": null,
        "localLanguage": {
            "territoryId": 2,
            "localLanguageName": "N/A",
            "localLanguageCode": null
        }
    },
    {
        "region": "Africa",
        "country": "Morocco (incl. Western Sahara)",
        "territory": null,
        "territoryCode": "MA",
        "t2": "AFR",
        "t3": "MA",
        "t4": null,
        "t5": null,
        "t6": null,
        "t7": null,
        "localLanguage": {
            "territoryId": 35,
            "localLanguageName": "Arabic, French",
            "localLanguageCode": "ARA, FRE"
        }
    },
    {
        "region": "Africa",
        "country": "Morocco (incl. Western Sahara)",
        "territory": "Morocco (excl. Western Sahara)",
        "territoryCode": "MAXEH",
        "t2": "AFR",
        "t3": "MA",
        "t4": "MAXEH",
        "t5": null,
        "t6": null,
        "t7": null,
        "localLanguage": {
            "territoryId": 36,
            "localLanguageName": "Arabic, French",
            "localLanguageCode": "ARA, FRE"
        }
    },
    {
        "region": "Africa",
        "country": "Morocco (incl. Western Sahara)",
        "territory": "Western Sahara",
        "territoryCode": "EH",
        "t2": "AFR",
        "t3": "MA",
        "t4": "EH",
        "t5": null,
        "t6": null,
        "t7": null,
        "localLanguage": {
            "territoryId": 37,
            "localLanguageName": "Arabic, French",
            "localLanguageCode": "ARA, FRE"
        }
    }
]


5 commentaires

Dans vos données d'origine, vous êtes en pleine forme pour vous connecter à elasticsearch! À propos de la façon d'écrire la conversion, qu'avez-vous essayé qui vous fait trébucher?


@Doug, j'ai essayé les var theaters = [], city = []; for (var key in data) {var output = {}, city_data = {}; city_data.desc = data [clé] .city; output.id = data [clé] .theatreId; output.desc = data [clé] .theatreDescription; theatres.push (sortie); city.push (city_data)} Mais vous cherchez à faire de même en utilisant les méthodes par défaut


pourquoi avez-vous pour le country une structure d'objet différente, une avec t2 et une autre avec sous- subTerritory ? quelle est la raison?


veuillez ajouter un résultat réaliste à partir des données.


@ stackUser67 pourriez-vous s'il vous plaît spécifier les clés exactes du json d'entrée sur lesquelles vous voulez faire un groupe? Selon ce que je comprends de la sortie json que vous avez publiée, vous regroupez par «région» au niveau 1 et tous les pays de la même région se trouvent à l'intérieur du tableau référencé par la clé «pays». Et comment prévoyez-vous exactement de remplir 'subTerritory' dans votre json de sortie final?


6 Réponses :


0
votes

essaye ça

var data = [
  {
    city: 'LAKE GENEVA',
    state: 'WISCONSIN',
    theatreId: '000080',
    theatreDescription: 'GENEVA 4'
  },
  {
    city: 'BURLINGTON',
    state: 'WISCONSIN',
    theatreId: 'c05364',
    theatreDescription: 'PLAZA THEATRE 4'
  }
]

let group = data.reduce((r, a) => {
 r[a.state] = [...r[a.state] || [], a];
 return r;
}, {});

let nowObj ={};
for (let [key, value] of Object.entries(group)) {
  let groupNew = value.reduce((r, a) => {
      r[a.city] = [...r[a.city] || [], a];
      return r;
  }, {});
  nowObj[key] =groupNew;
}
let mainObj=[];
for (let [key, value] of Object.entries(nowObj)) {
  let obj ={};
  let city =[];
  for (let [key2, value2] of Object.entries(value)) {
    let cityObj ={};
    cityObj['desc'] =key2;
    cityObj['theatres'] =value2.map(item => {
      const container = {};
      container['id'] = item.theatreId;
      container['des'] = item.theatreDescription;
      return container;
    });
    city.push(cityObj);
  }
  obj['state'] =key;
  obj['city'] =city;
  mainObj.push(obj);
}

console.log(mainObj);


0 commentaires

0
votes

J'utiliserais un constructeur ou une classe. Je préfère les constructeurs, en raison de leur capacité à maintenir des variables privées avec moins de travail (non montré dans l'exemple) . Vérifiez-le!

function Theaterminal(dataArray){
  this.states = [];
  this.getPlace = (state, city)=>{
    let sx = new RegExp('^'+state+'$', 'i'), cx;
    if(city)cx = new RegExp('^'+city+'$', 'i');
    for(let s of this.states){
      if(s.state.match(sx)){
        if(city){
          for(let c of s.cities){
            if(c.city.match(cx)){
              return c;
            }
          }
          return false;
        }
        else{
          return s;
        }
      }
    }
    return false;
  }
  this.parse = data=>{
    let s = this.states, w, q, t, c, b;
    s.splice(0);
    data.forEach(o=>{
      w = o.state; q = this.getPlace(w); t = {id:o.theatreId, desc:o.theatreDescription}; c = {city:o.city, theaters:[t]};
      if(q === false){
        s.push({state:w, cities:[c]});
      }
      else{
        b = this.getPlace(w, o.city);
        if(b === false){
          q.cities.push(c);
        }
        else{
          b.theaters.push(t);
        }
      }
    });
    return s;
  }
  this.parse(dataArray);
}
const data = [
  {
    city: 'LAKE GENEVA',
    state: 'WISCONSIN',
    theatreId: '000080',
    theatreDescription: 'GENEVA 4'
  },
  {
    city: 'BURLINGTON',
    state: 'WISCONSIN',
    theatreId: 'c05364',
    theatreDescription: 'PLAZA THEATRE 4'
  },
  {
    city: 'LAKE GENEVA',
    state: 'WISCONSIN',
    theatreId: 'fakeId',
    theatreDescription: 'fake theater'
  },
  {
    city: 'SEATTLE',
    state: 'WASHINGTON',
    theatreId: 'example id',
    theatreDescription: 'why bother'
  }
];
const terminal = new Theaterminal(data);
console.log(terminal.states);
console.log('-----------------------check this out--------------------');
console.log(terminal.getPlace('wisconsin', 'lake geneva'));

Notez que si vous passez un dataArray à un TheaterminalInstance.parse(here) , TheaterminalInstance.states changera également, sans perdre son association de référence Object. dataArray ne sera pas modifié.


0 commentaires

1
votes

Vous devez regrouper l'objet du tableau en utilisant Array.prototype.reduce en fonction de la propriété state , puis vérifier si une ville existe ou non, si elle existe, écraser avec les dernières valeurs, sinon le pousser dans le tableau de la city , de même vous devez vérifier le théâtre, à la fin, vous devez renvoyer l'accumulateur pour la prochaine itération.

const data1 = [{
    city: 'LAKE GENEVA',
    state: 'WISCONSIN',
    theatreId: '000080',
    theatreDescription: 'GENEVA 4'
  },
  {
    city: 'BURLINGTON',
    state: 'WISCONSIN',
    theatreId: 'c05364',
    theatreDescription: 'PLAZA THEATRE 4'
  }
];

const data2 = [{
  city: 'MIAMI',
  state: 'FLORIDA',
  theatreId: 'c05170',
  theatreDescription: 'DOLPHIN 24'
}, {
  city: 'MIAMI',
  state: 'FLORIDA',
  theatreId: '000306',
  theatreDescription: 'CMX BRICKELL CITY CENTRE 10'
}];

const reduceCityTheaters = (arr) => Object.values(arr.reduce((acc, curr) => {
  // Deconstruct needed properties
  const { state, city, theatreId, theatreDescription } = curr;
  
  // Check if state not present
  if (!acc[state]) {
    let obj = {};
    obj.state = state;
    obj.city = [{
      desc: city,
      theatres: [{
        id: theatreId,
        desc: theatreDescription
      }]
    }];

    acc[state] = obj;
  } else { // Check if state is present
    acc[state].city = acc[state].city || [];
    const foundCity = acc[state].city.find(x => x.desc === city);

    // Check if city exists or not if not push it
    if (!foundCity) {
      acc[state].city.push({
        desc: city,
        theatres: [{
          id: theatreId,
          desc: theatreDescription
        }]
      });
    } else {
      const foundTheater = foundCity.theatres.find(x => x.id === theatreId);

      // Check if theatre exists or not if not push it
      if (!foundTheater) {
        foundCity.theatres.push({
          id: theatreId,
          desc: theatreDescription
        });
      } else {
        foundTheater.id = theatreId;
        foundTheater.desc = theatreDescription;
      }
    }
  }

  return acc;
}, {}));


const res1 = reduceCityTheaters(data1);
const res2 = reduceCityTheaters(data2);

console.log('1', res1);
console.log('2', res2);


1 commentaires

Mais cela fonctionne-t-il pour l'objet ci-dessous? Je ne pense pas. [{city: 'MIAMI', state: 'FLORIDA', theatreId: 'c05170', theatreDescription: 'DOLPHIN 24'}, {city: 'MIAMI', state: 'FLORIDA', theatreId: '000306', theatreDescription: ' CMX BRICKELL CITY CENTER 10 '}]



1
votes

Je commencerais par un groupBy très générique qui prend en charge l'imbrication de plusieurs groupes en fonction d'un tableau de clés:

.as-console-wrapper { min-height: 100% }

Remarque: vous pouvez probablement trouver des versions bien entretenues, mieux testées et plus performantes de ces méthodes utilitaires dans des bibliothèques frontales comme Ramda ou lodash.

Vous pouvez désormais regrouper vos données en utilisant:

const AllKey = Symbol();

// Utils
const mapObj = (f, obj) => Object.fromEntries(
  Object.entries(obj).map(([k, v]) => [ k, f(v) ])
)

const groupByKey = (k, xs) => xs
  .map(x => [x[k], x])
  .reduce(
    (gs, [k, v]) => Object.assign(gs, { [k]: (gs[k] || []).concat([v]) }),
    {}
  );

const groupByKeys = ([k, ...ks], xs) => k
  ? Object.assign(mapObj(
      ys => groupByKeys(ks, ys),
      groupByKey(k, xs)
    ), { [AllKey]: xs })
  : xs;

// App
const keys = ["region", "country", "t2", "t3", "t4", "t5", "t6", "t7"]
const groups = groupByKeys(keys, getData());

// Transformers for all layers of grouping
const Region = ({ [AllKey]: [ { region } ], ...countries }) => ({ 
  region,
  country: Object.values(countries).map(Country).filter(Country.notEmpty)
});

Region.notEmpty = ({ region }) => region !== null;

const Country = ({ [AllKey]: [ { country } ], ...t2}) => ({
  desc: country,
  t2: Object.values(t2).map(T2)
});

Country.notEmpty = ({ desc }) => desc !== null;

const T2 = ({ [AllKey]: [ t2 ], ...t3 }) => ({
  id: t2.t2,
  localLanguageName: t2.localLanguage.localLanguageName,
  localLanguageCode: t2.localLanguage.localLanguageCode,
  t3: Object.values(t3).map(T3)
})

const T3 = ({ [AllKey]: [ { t3 } ], ...t4 }) => ({
  id: t3 // Etc.
})

const transform = groups => Object
  .values(groups)
  .map(Region)
  .filter(Region.notEmpty);

console.log(JSON.stringify(transform(groups), null, 2));

function getData() {
  return [{
    "region": null,
    "country": null,
    "territory": "Worldwide",
    "territoryCode": "ALL",
    "t2": null,
    "t3": null,
    "t4": null,
    "t5": null,
    "t6": null,
    "t7": null,
    "localLanguage": {
      "territoryId": 1,
      "localLanguageName": "N/A",
      "localLanguageCode": null
    }
  }, {
    "region": "Africa",
    "country": "Madagascar",
    "territory": null,
    "territoryCode": "MG",
    "t2": "AFR",
    "t3": "MG",
    "t4": null,
    "t5": null,
    "t6": null,
    "t7": null,
    "localLanguage": {
      "territoryId": 30,
      "localLanguageName": "Malagasy, French",
      "localLanguageCode": "MLG, FRE"
    }
  }, {
    "region": "Africa",
    "country": null,
    "territory": null,
    "territoryCode": "AFR",
    "t2": "AFR",
    "t3": null,
    "t4": null,
    "t5": null,
    "t6": null,
    "t7": null,
    "localLanguage": {
      "territoryId": 2,
      "localLanguageName": "N/A",
      "localLanguageCode": null
    }
  }, {
    "region": "Africa",
    "country": "Morocco (incl. Western Sahara)",
    "territory": null,
    "territoryCode": "MA",
    "t2": "AFR",
    "t3": "MA",
    "t4": null,
    "t5": null,
    "t6": null,
    "t7": null,
    "localLanguage": {
      "territoryId": 35,
      "localLanguageName": "Arabic, French",
      "localLanguageCode": "ARA, FRE"
    }
  }, {
    "region": "Africa",
    "country": "Morocco (incl. Western Sahara)",
    "territory": "Morocco (excl. Western Sahara)",
    "territoryCode": "MAXEH",
    "t2": "AFR",
    "t3": "MA",
    "t4": "MAXEH",
    "t5": null,
    "t6": null,
    "t7": null,
    "localLanguage": {
      "territoryId": 36,
      "localLanguageName": "Arabic, French",
      "localLanguageCode": "ARA, FRE"
    }
  }, {
    "region": "Africa",
    "country": "Morocco (incl. Western Sahara)",
    "territory": "Western Sahara",
    "territoryCode": "EH",
    "t2": "AFR",
    "t3": "MA",
    "t4": "EH",
    "t5": null,
    "t6": null,
    "t7": null,
    "localLanguage": {
      "territoryId": 37,
      "localLanguageName": "Arabic, French",
      "localLanguageCode": "ARA, FRE"
    }
  }]
};

La prochaine étape serait la partie la plus difficile de la transformation au format souhaité et de la gestion de tous les chemins null .

Pour rendre cela un peu plus facile et plus performant, j'inclus un tableau de tous les éléments sur chaque couche de regroupement:

// Transformers for all layers of grouping
const Region = ({ [AllKey]: [ { region } ], ...countries }) => ({ 
  region,
  country: Object.values(countries).map(Country).filter(Country.notEmpty)
});

Region.notEmpty = ({ region }) => region !== null;

const Country = ({ [AllKey]: [ { country } ], ...t2}) => ({
  desc: country,
  t2: Object.values(t2).map(T2)
});

Country.notEmpty = ({ desc }) => desc !== null;

const T2 = ({ [AllKey]: [ t2 ], ...t3 }) => ({
  id: t2.t2,
  localLanguageName: t2.localLanguage.localLanguageName,
  localLanguageCode: t2.localLanguage.localLanguageCode,
  t3: Object.values(t3).map(T3)
})

const T3 = ({ [AllKey]: [ { t3 } ], ...t4 }) => ({
  id: t3 // Etc.
})

Nous pouvons maintenant parcourir notre regroupement en utilisant Object.values , Array.prototype.map et quelques destructures:

const transform = groups => Object
  .values(groups)
  .map(
    ({ [AllKey]: allElementsInThisLayer, ...childGrouping }) => { /* ... */ }
  );

Il ne reste plus qu'à définir la logique de transformation de chaque couche. C'est là que, pour être honnête, je ne comprends pas vraiment le résultat souhaité. J'ai implémenté les premières couches, mais peut-être que vous pouvez faire un meilleur travail vous-même, maintenant que vous avez des données structurées avec lesquelles travailler:

const groupByKeys = ([k, ...ks], xs) => k
  ? Object.assign(mapObj(
      ys => groupByKeys(ks, ys),
      groupByKey(k, xs)
    ), { [AllKey]: xs })
  : xs;

Voici un extrait de code exécutable. Continuez le travail au // Etc. commentaire 🠙 ‚

const groups = groupByKeys(
  ["region", "country", "t2", "t3", "t4", "t5", "t6", "t7"],
  your_data
);
// Takes an array of keys and array of objects and returns a nested grouping
const groupByKeys = ([k, ...ks], xs) => k
  ? mapObj(
      ys => groupByKeys(ks, ys),
      groupByKey(k, xs)
    )
  : xs;

// Groups an array based by a key
const groupByKey = (k, xs) => xs
  .reduce(
    (gs, x) => Object.assign(gs, { [x[k]]: (gs[k] || []).concat([x]) }),
    {}
  );

// Utility to map a function over the values of an object
const mapObj = (f, obj) => Object.fromEntries(
  Object.entries(obj).map(([k, v]) => [ k, f(v) ])
);


0 commentaires

1
votes

Cette réponse propose deux solutions:

  • Une approche centristique de niveau avec des fonctions personnalisées pour traiter chaque niveau.
  • Une approche rapide avec un tableau donné de clés et un jeu de résultats uniforme.

Fonctions personnalisées pour chaque niveau avec court-circuit

Cette approche adopte une vue différente à chaque niveau et permet de terminer l'itération pour un certain nœud.

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

Le code ci-dessus itère l'ensemble de données donné et utilise un tableau de fonction de regroupement qui renvoie une valeur vraie ou fausse . En cas de valeur fausse , l'itération s'arrête à ce niveau.

const
    keys = ['region', 'country', 't2', 't3', 't4', 't5', 't6', 't7'],
    data = [{ region: null, country: null, territory: "Worldwide", territoryCode: "ALL", t2: null, t3: null, t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 1, localLanguageName: "N/A", localLanguageCode: null } }, { region: "Africa", country: "Madagascar", territory: null, territoryCode: "MG", t2: "AFR", t3: "MG", t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 30, localLanguageName: "Malagasy, French", localLanguageCode: "MLG, FRE" } }, { region: "Africa", country: null, territory: null, territoryCode: "AFR", t2: "AFR", t3: null, t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 2, localLanguageName: "N/A", localLanguageCode: null } }, { region: "Africa", country: "Morocco (incl. Western Sahara)", territory: null, territoryCode: "MA", t2: "AFR", t3: "MA", t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 35, localLanguageName: "Arabic, French", localLanguageCode: "ARA, FRE" } }, { region: "Africa", country: "Morocco (incl. Western Sahara)", territory: "Morocco (excl. Western Sahara)", territoryCode: "MAXEH", t2: "AFR", t3: "MA", t4: "MAXEH", t5: null, t6: null, t7: null, localLanguage: { territoryId: 36, localLanguageName: "Arabic, French", localLanguageCode: "ARA, FRE" } }, { region: "Africa", country: "Morocco (incl. Western Sahara)", territory: "Western Sahara", territoryCode: "EH", t2: "AFR", t3: "MA", t4: "EH", t5: null, t6: null, t7: null, localLanguage: { territoryId: 37, localLanguageName: "Arabic, French", localLanguageCode: "ARA, FRE" } }],
    result = data
        .reduce((t, o) => {
            const groups = keys.filter((flag => k => flag = flag && o[k])(true));
            groups
                .reduce(function (r, k) {
                    let id;
                    ({ [k]: id, ...o } = o);
                    if (!r[id]) {
                        r[id] = { _: [] };
                        r._.push({ id, children: r[id]._ });
                    }
                    return r[id];
                }, t)._.push(o);
            return t;
        }, { _: [] })
        ._;
 
   console.log(result);

Une fonction de regroupement comprend au moins quatre ou cinq parties.

  1. La signature de fonction contient l'objet source ou un sous-ensemble via la déstructuration et une cible. La cible peut être un tableau ou un objet.

  2. Un contrôle si la valeur de regroupement existe et sinon elle retourne. Cela termine également l'itération pour tous les groupes suivants. Ce point est facultatif.

  3. Une recherche pour trouver le bon objet avec le groupe recherché.

  4. Un contrôle si le groupe existe et sinon il crée un nouveau groupe avec les propriétés voulues

  5. Une valeur de retour de la fonction, un tableau ou un objet, selon la fonction de regroupement suivante.


Le principal avantage de cette approche est la possibilité de gérer différents niveaux avec des structures personnalisées et la possibilité d'omettre des valeurs de regroupement vides / non données.

Le principal inconvénient est d'avoir une fonction pour chaque niveau.

{ id, children: [] }
.as-console-wrapper { max-height: 100% !important; top: 0; }

Approche uniforme rapide, chaque niveau a la même structure

const
    groups = [
        ({ region }, target) => {
            if (!region) return;
            let temp = target.find(q => q.region === region);
            if (!temp) target.push(temp = { region, country: [] });
            return temp.country;
        },
        ({ country: desc }, target) => {
            if (!desc) return;
            let temp = target.find(q => q.desc === desc);
            if (!temp) target.push(temp = { desc, t2: [] });
            return temp.t2;
        },
        ({ t2: id, localLanguage: { localLanguageName, localLanguageCode } }, target) => {
            if (!id) return;
            let temp = target.find(q => q.id === id);
            if (!temp) target.push(temp = { id, localLanguageName, localLanguageCode, t3: [] });
            return temp.t3;
        },
        ({ t3: id }, target) => {
            if (!id) return;
            let temp = target.find(q => q.id === id);
            if (!temp) target.push(temp = { id, t4: [] });
            return temp.t4;
        },
        ({ t4: id }, target) => {
            if (!id) return;
            let temp = target.find(q => q.id === id);
            if (!temp) target.push(temp = { id, t5: [] });
            return temp.t5;
        }
    ],
    data = [{ region: null, country: null, territory: "Worldwide", territoryCode: "ALL", t2: null, t3: null, t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 1, localLanguageName: "N/A", localLanguageCode: null } }, { region: "Africa", country: "Madagascar", territory: null, territoryCode: "MG", t2: "AFR", t3: "MG", t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 30, localLanguageName: "Malagasy, French", localLanguageCode: "MLG, FRE" } }, { region: "Africa", country: null, territory: null, territoryCode: "AFR", t2: "AFR", t3: null, t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 2, localLanguageName: "N/A", localLanguageCode: null } }, { region: "Africa", country: "Morocco (incl. Western Sahara)", territory: null, territoryCode: "MA", t2: "AFR", t3: "MA", t4: null, t5: null, t6: null, t7: null, localLanguage: { territoryId: 35, localLanguageName: "Arabic, French", localLanguageCode: "ARA, FRE" } }, { region: "Africa", country: "Morocco (incl. Western Sahara)", territory: "Morocco (excl. Western Sahara)", territoryCode: "MAXEH", t2: "AFR", t3: "MA", t4: "MAXEH", t5: null, t6: null, t7: null, localLanguage: { territoryId: 36, localLanguageName: "Arabic, French", localLanguageCode: "ARA, FRE" } }, { region: "Africa", country: "Morocco (incl. Western Sahara)", territory: "Western Sahara", territoryCode: "EH", t2: "AFR", t3: "MA", t4: "EH", t5: null, t6: null, t7: null, localLanguage: { territoryId: 37, localLanguageName: "Arabic, French", localLanguageCode: "ARA, FRE" } }],
    result = data.reduce((r, o) => {
        let p = r;
        groups.every(fn => p = fn(o, p));
        return r;
    }, []);
   console.log(result);

C'est la structure du résultat pour chaque niveau. id reflète la valeur réelle du groupe et children contient d'autres objets de la même structure ou l'objet final.

Cette approche prend un objet pour toutes les clés du niveau et un résultat _ intérieur.

Il commence par filtrer les groupes supérieurs avec une valeur et réduit les groupes en extrayant la propriété level de l'objet (final).

Si le groupe au niveau réel n'existe pas, une nouvelle propriété avec un tableau est créée. Cet objet contient une seule propriété _ avec un tableau vide. Ce tableau partage la même référence d'objet que dans l'objet visible plus tard pour les enfants de ce niveau.

À la fin de la réduction, l'objet final sans propriétés visitées est poussé.

Enfin, la propriété de soulignement est renvoyée, car elle contient tous les groupes imbriqués.


Le principal avantage de cette approche est d'ajouter instantanément les regroupements voulus par les clés données.

Le principal inconvénient est d'obtenir un résultat uniforme qui n'offre aucune personnalisation pour un certain niveau. Il ne filtre pas les objets indésirables.

({ region }, target) => {                                   // 1
    if (!region) return;                                    // 2
    let temp = target.find(q => q.region === region);       // 3
    if (!temp) target.push(temp = { region, country: [] }); // 4
    return temp.country;                                    // 5
}
result = data.reduce((r, o) => {
    let p = r;
    groups.every(fn => p = fn(o, p));
    return r;
}, []);

Il est difficile de comprendre quelle partie, si ou une partie de l'ensemble de données donné doit être couverte par une solution canonique et quelle opération cible recherche. Par conséquent, cette réponse se compose de deux parties, mais si un indice était donné, n'importe qui en profiterait.


0 commentaires

0
votes

Ce problème réclame une solution récursive . J'ai apporté une solution générique au problème, mais cela signifie passer sous silence certaines des variations entre la façon dont vous exprimez différents niveaux.

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

La principale variation entre les niveaux dans votre sortie souhaitée est le nom de la propriété qui identifie la catégorie ... il est diversement «id», «desc» ou «region» dans le cas du niveau supérieur. Cela rendrait les choses moins élégantes, mais c'est évidemment possible.

Voici un code exécutable:

const sampleData = [
  {
    region: null,
    country: null,
    territory: "Worldwide",
    territoryCode: "ALL",
    t2: null,
    t3: null,
    t4: null,
    t5: null,
    t6: null,
    t7: null,
    localLanguage: {
      territoryId: 1,
      localLanguageName: "N/A",
      localLanguageCode: null,
    },
  },
  {
    region: "Africa",
    country: "Madagascar",
    territory: null,
    territoryCode: "MG",
    t2: "AFR",
    t3: "MG",
    t4: null,
    t5: null,
    t6: null,
    t7: null,
    localLanguage: {
      territoryId: 30,
      localLanguageName: "Malagasy, French",
      localLanguageCode: "MLG, FRE",
    },
  },
  {
    region: "Africa",
    country: null,
    territory: null,
    territoryCode: "AFR",
    t2: "AFR",
    t3: null,
    t4: null,
    t5: null,
    t6: null,
    t7: null,
    localLanguage: {
      territoryId: 2,
      localLanguageName: "N/A",
      localLanguageCode: null,
    },
  },
  {
    region: "Africa",
    country: "Morocco (incl. Western Sahara)",
    territory: null,
    territoryCode: "MA",
    t2: "AFR",
    t3: "MA",
    t4: null,
    t5: null,
    t6: null,
    t7: null,
    localLanguage: {
      territoryId: 35,
      localLanguageName: "Arabic, French",
      localLanguageCode: "ARA, FRE",
    },
  },
  {
    region: "Africa",
    country: "Morocco (incl. Western Sahara)",
    territory: "Morocco (excl. Western Sahara)",
    territoryCode: "MAXEH",
    t2: "AFR",
    t3: "MA",
    t4: "MAXEH",
    t5: null,
    t6: null,
    t7: null,
    localLanguage: {
      territoryId: 36,
      localLanguageName: "Arabic, French",
      localLanguageCode: "ARA, FRE",
    },
  },
  {
    region: "Africa",
    country: "Morocco (incl. Western Sahara)",
    territory: "Western Sahara",
    territoryCode: "EH",
    t2: "AFR",
    t3: "MA",
    t4: "EH",
    t5: null,
    t6: null,
    t7: null,
    localLanguage: {
      territoryId: 37,
      localLanguageName: "Arabic, French",
      localLanguageCode: "ARA, FRE",
    },
  },
];

function nestedGrouper(
  data = sampleData,
  groupKeys = ["region", "country", "t2", "t3", "t4", "t5", "t6"]
) {
  function populate(group, remainingKeys, item) {
    if (remainingKeys.length === 1) return;
    const [thisKey, nextKey] = remainingKeys;

    const member =
      group.find((a) => a.id === item[thisKey]) ||
      (group.push(
        Object.assign({
          id: item[thisKey]
        },
        thisKey !== 'region' && thisKey !== 'country' && {
          localLanguageName: item.localLanguage.localLanguageName,
          localLanguageCode: item.localLanguage.localLanguageCode,
        })
      ),
      group[group.length - 1]);

    const subGroup = member[nextKey] || [];

    if (item[nextKey]) {
      populate(subGroup, remainingKeys.slice(1), item);
    }

    // no key for empty array
    if (subGroup.length > 0) {
      member[nextKey] = subGroup;
    }
  }

  return data.reduce(
    (result, item) => (populate(result, groupKeys, item), result),
    []
  );
}

console.log(JSON.stringify(nestedGrouper(), null, 4));
function nestedGrouper(
  data = sampleData,
  groupKeys = ["region", "country", "t2", "t3", "t4", "t5", "t6"]
) {
  function populate(group, remainingKeys, item) {
    if (remainingKeys.length === 1) return;
    const [thisKey, nextKey] = remainingKeys;

    const member =
      group.find((a) => a.id === item[thisKey]) ||
      (group.push(
        Object.assign({
          id: item[thisKey]
        },
        thisKey !== 'region' && thisKey !== 'country' && {
          localLanguageName: item.localLanguage.localLanguageName,
          localLanguageCode: item.localLanguage.localLanguageCode,
        })
      ),
      group[group.length - 1]);

    const subGroup = member[nextKey] || [];

    if (item[nextKey]) {
      populate(subGroup, remainingKeys.slice(1), item);
    }

    // no key for empty array
    if (subGroup.length > 0) {
      member[nextKey] = subGroup;
    }
  }

  return data.reduce(
    (result, item) => (populate(result, groupKeys, item), result),
    []
  );
}

La vérité est que j'ai `` codé en dur '' un aspect, qui est le localLanguageName et localLanguageCode ... Je dois noter que nous dépendons des données d'entrée pour être cohérentes pour ces propriétés, car si le langage est différent pour les objets avec des valeurs t3 différentes mais les mêmes valeurs t2 , il n'y a aucun moyen de décider quelles devraient être ces propriétés au niveau t2 .


0 commentaires