4
votes

JS: supprimer la clé d'objet si toutes les valeurs imbriquées sont nulles

À partir d'une structure de données donnée (fichier json), j'ai essentiellement besoin de rendre une table. Les lignes et / ou colonnes vides ne doivent pas être rendues. Je suis assez nouveau dans JavaScript et j'ai essayé différentes approches (conversion en tableau et utilisation de .map (), reduction (), .filter (), lodash etc.) sans succès. Je ne sais même pas quelle serait la meilleure façon de s'attaquer au problème. (Ou quels seraient les termes de recherche possibles.)

Ni les «clés de ligne» (par exemple: mo, tu, nous, th, fr) ni les «clés de colonne» (john, hane, doe) ne sont connues et peuvent varier.

Exemple complet: https://jsbin.com/rafeyasena/edit? js, sortie

"groupA": {
    "mo": { "john": 8, "jane": 5, },
    "tu": { "john": 8, "jane": 5, },
    "we": { "john": 5, "jane": 9, },
    "th": { "john": 6, "jane": 3, }
  }

Structure de données résultante possible

   | John | Jane |
---|------|--------
mo |  8   |  5   |
tu |  8   |  5   |
we |  5   |  9   |
th |  6   |  3   |

Résultat attendu (Front-end, React) :

const header = ["John", "Jane"];
const content = [
 "mo": {[ 8, 5 ]},
 "tu": {[ 8, 5 ]},
 "we": {[ 5, 9 ]},
 "th": {[ 6, 3 ]}
]

Ce que j'ai essayé jusqu'à présent: J'ai pu supprimer toutes les valeurs de null et la clé correspondante, si elle ne contient plus de clés / valeurs ( Supprimer les valeurs nulles dans les objets javascript imbriqués ) - me conduisant avec le défi de trouver toutes les clés restantes pour construire l'en-tête de la table. (Dans l'exemple ci-dessous, ce ne serait que John et Jane - Donc, fondamentalement, un moyen d'itérer sur toutes les clés et de consigner chaque clé qui existe au moins une fois). Donc mes données actuelles ressemblent à ceci (mais je ne suis pas sûr que ce soit le meilleur moyen):

"groupA": {
    "mo": { "john": 8, "jane": 5, "doe": null },
    "tu": { "john": 8, "jane": 5, "doe": null },
    "we": { "john": 5, "jane": 9, "doe": null },
    "th": { "john": 6, "jane": 3, "doe": null },
    "fr": { "john": null, "jane": null, "doe": null }
  }


0 commentaires

3 Réponses :


0
votes

Je représenterais simplement les données sous forme de tableau 2D (ce qui facilite le rendu):

 const columnNames = [""];
 const rows = [columnNames];

  for(const [rowName, values] of Object.entries(groupA)) {
     const row = [rowName];
     for(const [columnName, value] of Object.entries(values)) {
        let position = columnNames.indexOf(columnName);
        if(value === null) continue;
        if(position === -1)
          position = columnNames.push(columnName) - 1;
        row[position] = value;
     }
     rows.push(row);
 }

 // just some debugging:   
 console.log( rows.map(row => row.map(it => (it || "").padStart(10)).join("|")).join("\n") );


1 commentaires

Merci! Votre réponse est bien plus compacte que tout ce que j'aurais pu écrire. Comprendre la déstructuration des tableaux pour la première fois. J'essaye de comprendre la partie "let position = columnNames.indexOf (columnName)". Actuellement, le code génère toujours des lignes vides (sans aucune valeur) - mais cela devrait être facile à corriger. Merci beaucoup pour vos efforts!



0
votes

Je pense que la création de ce dernier format (avec les nulls supprimés) est une première étape très utile. À partir de là, vous pouvez écrire quelque chose comme ceci pour l'intégrer dans une variante de votre format cible:

{
  headers: ['john', 'jane'],
  content: {
    mo: [8, 5],
    tu: [8, 5],
    we: [5, 9],
    th: [6, 3]
  }
}

Notez que la cible est un peu différente de votre requête, car votre exemple de contenu n'est pas légal JS ( {[8, 5]} n'a pas de sens) mais Je pense qu'il en capture l'esprit, renvoyant quelque chose comme:

const uniqKeys = (obj) => [... new Set(Object.values(obj).flatMap(Object.keys))]

const transform = (group, headers = uniqKeys(group)) => ({
  headers,
  content: Object.entries(group).reduce(
    (a, [k, v]) => ({...a, [k]: headers.map(h => v[h])}),
    {}
  )
})


const groupA = {mo: {john: 8, jane: 5}, tu: {john: 8, jane: 5}, we: {john: 5, jane: 9}, th: {john: 6, jane: 3}}

console.log(transform(groupA))

Notez que cette fonction est un peu plus générale que les exigences, car vous pourriez lui fournir une liste d'en-têtes et extrayez-les uniquement des données.


0 commentaires

0
votes

Jetez un œil à object-scan . Cela rend ce genre de choses relativement facile une fois que vous comprenez comment cela fonctionne. Voici comment vous répondriez à vos questions

const objectScan = require('object-scan');

const isNullObject = (obj) => (
  obj instanceof Object
  && !Array.isArray(obj)
  && Object.values(obj).every((e) => e === null)
);

const prune = (data) => objectScan(['**'], {
  rtn: 'count',
  filterFn: ({ value, parent, property }) => {
    if (isNullObject(value)) {
      delete parent[property];
      return true;
    }
    return false;
  }
})(data);

const stats = {
  groupA: {
    mo: { john: 8, jane: 5, doe: null },
    tu: { john: 8, jane: 5, doe: null },
    we: { john: 5, jane: 9, doe: null },
    th: { john: 6, jane: 3, doe: null },
    fr: { john: null, jane: null, doe: null }
  }
};

console.log(prune(stats)); // return number of replaces
// => 1

console.log(stats);
// => { groupA:
//    { mo: { john: 8, jane: 5, doe: null },
//      tu: { john: 8, jane: 5, doe: null },
//      we: { john: 5, jane: 9, doe: null },
//      th: { john: 6, jane: 3, doe: null } } }


0 commentaires