1
votes

Supprimer récursivement les valeurs nulles d'un objet JavaScript

J'ai recherché ceci mais je n'ai pas trouvé de réponse satisfaisante, je publie donc ma propre réponse ici.

En gros, je voulais une fonction qui:

  • prend un objet comme argument
  • supprime de manière récursive les propriétés dont les valeurs sont null , undefined , [] , {} ou ''
  • conserve les valeurs 0 et false
  • renvoie un nouvel objet avec ces propriétés supprimées
  • de préférence dans un style fonctionnel sans mutations

3 commentaires

que doit-il se passer avec les éléments nuls d'un tableau?


par exemple. [null, null, null] est un tableau non vide, mais aussi un objet, qui a trois propriétés, qui sont nul . Que faire ici? Aussi, pour une entrée de par ex. {x: {y: null}} , je suppose que la suppression doit être effectuée en profondeur d'abord, et que le résultat doit être {} et non {x: {}} ?


en fonction de mes besoins, si un objet avait une propriété qui était [null, null, null] , le tableau entier et la propriété devraient être supprimés. Pour {x: {y: null}} , nous devrions obtenir {}


4 Réponses :


0
votes

Voici ce que j'ai trouvé (merci à Nina pour les données de test):

function stripNullsFromObject(obj) {
  function stripNullsFromArray(arr) {
    return arr.reduce((acc, cur) => {
      if (typeof cur === 'object' && !Array.isArray(cur) && cur !== null) {
        const nestedObj = stripNullsFromObject(cur);
        if (Object.entries(nestedObj).length > 0) {
          return acc.concat(nestedObj);
        }
      }
      if (Array.isArray(cur)) {
        const nestedArr = stripNullsFromArray(cur);
        if (nestedArr.length > 0) {
          return acc.concat(nestedArr);
        }
      }
      if (typeof cur !== 'object' && (!!cur || cur === 0 || cur === false)) {
        return acc.concat(cur);
      }
      return acc;
    }, []);
  }

  return Object.entries(obj).reduce((acc, [key, val]) => {
    if (typeof val === 'object' && !Array.isArray(val) && val !== null) {
      const nestedObj = stripNullsFromObject(val);
      if (Object.entries(nestedObj).length > 0) {
        return {
          ...acc,
          [key]: nestedObj,
        };
      }
    }
    if (Array.isArray(val)) {
      const nestedArr = stripNullsFromArray(val);
      if (nestedArr.length > 0) {
        return {
          ...acc,
          [key]: nestedArr,
        };
      }
    }
    if (typeof val !== 'object' && (!!val || val === 0 || val === false)) {
      return {
        ...acc,
        [key]: val,
      };
    }
    return acc;
  }, {});
}

const data = {
  emptyArray: [],
  arrayWithNullish: [null, {}, [], undefined],
  null: null,
  undefined: undefined,
  emptyString: '',
  zero: 0,
  false: false,
  true: true,
  emptyObject: {},
  objectWithNullish: { null: null, emptyArray: [], undefined: undefined },
  nestedObject: {
    nestedObject: {
      null: null,
      one: 1,
      emptyObject: {}
    },
    nestedEmptyArray: [[], [[]]]
  }
};

console.log(stripNullsFromObject(data))

Je suis curieux de savoir si quelqu'un d'autre a une autre méthode ou des suggestions pour améliorer cela.


1 commentaires

Cliquez sur modifier, puis sur [<>] éditeur d'extraits de code et créez un exemple reproductible minimal



1
votes

Vous pouvez séparer les trois types de données dans

  • tableau,
  • objet
  • valeur primitive

et obtenez le sous-ensemble souhaité en réduisant les types de données complexes et en vérifiant les valeurs primitives.

.as-console-wrapper { max-height: 100% !important; top: 0; }
const
    isNullish = x => [
        v => v === '',
        v => v === null,
        v => v === undefined,
        v => v && typeof v === 'object' && !Object.keys(v).length
    ].some(f => f(x)),
    getArray = array => {
        var temp = array.reduce((r, v) => {
                v = getNotNullish(v);
                if (v !== undefined) r.push(v);
                return r;
            }, []);

        return temp.length ? temp : undefined;
    },
    getObject = object => {
        var hasValues = false,
            temp = Object.entries(object).reduce((r, [k, v]) => {
                v = getNotNullish(v);
                if (v !== undefined) {
                    r[k] = v;
                    hasValues = true;
                }
                return r;
            }, {});

        return hasValues ? temp : undefined;
    },
    getNotNullish = value => {
        if (Array.isArray(value)) return getArray(value);
        if (value && typeof value === 'object') return getObject(value);
        return isNullish(value) ? undefined : value;
    };


var data = {
        emptyArray: [],
        arrayWithNullish: [null, {}, [], undefined],
        null: null,
        undefined: undefined,
        emptyString: '',
        zero: 0,
        false: false,
        true: true,
        emptyObject: {},
        objectWithNullish: { null: null, emptyArray: [], undefined: undefined },
        nestedObject: {
            nestedObject: {
                null: null,
                one: 1,
                emptyObject: {}
            },
            nestedEmptyArray: [[], [[]]]
        }
    };

console.log(getNotNullish(data));


0 commentaires

1
votes

Voici ce que j'ai trouvé. (Merci à Nina pour avoir fourni un échantillon;)

<script>
var data = {
    emptyArray: [],
    arrayWithNullish: [null, {}, [], undefined],
    null: null,
    undefined: undefined,
    emptyString: '',
    zero: 0,
    false: false,
    true: true,
    emptyObject: {},
    objectWithNullish: { null: null, emptyArray: [], undefined: undefined },
    nestedObject: {
        nestedObject: {
            null: null,
            one: 1,
            emptyObject: {}
        },
        nestedEmptyArray: [[], [[]]]
    }
};
</script>
const is_obj = x => x !== null && typeof x === 'object';
const is_arr = x => Array.isArray(x);

const nullish = x =>

  (   typeof x !== 'number'
  &&  typeof x !== 'boolean'
  &&  typeof x !== 'function'
  )

  &&

  (   x === undefined
  ||  x === null
  ||  x === ''
  ||  Object.values(x).reduce((res, x) =>
        res && nullish(x), true)
  );

const clean = x =>
  [x]
    .map(x => Object.entries(x))
    .map(x => x.map(([k, v]) =>
        is_arr(v) ? [ k
                    , v.map(vv => is_obj(vv) ? clean(vv) : vv)
                    ]
      : is_obj(v) ? [ k
                    , clean(v)
                    ]
                  : [ k
                    , v
                    ]))
    .map(x => x.filter(([k, v]) => !nullish(v)))
    .map(x => Object.fromEntries(x))
    .pop();

console.log(clean(data));


1 commentaires

Très élégant! Ressemble presque à Haskell. :-)



0
votes

J'utilise l'exemple de données de l'une des réponses ici.

var data = {
    emptyArray: [],
    arrayWithNullish: [null, {}, [], undefined],
    null: null,
    undefined: undefined,
    emptyString: '',
    zero: 0,
    false: false,
    true: true,
    emptyObject: {},
    objectWithNullish: { null: null, emptyArray: [], undefined: undefined },
    nestedObject: {
        nestedObject: {
            null: null,
            one: 1,
            emptyObject: {}
        },
        nestedEmptyArray: [[], [[]], 6]
    }
}; 

function clean(data){
    return (function inner(data, output){
        if (!isObject(data)){
        return data;
      }
      Object.keys(data).forEach(function(key){
      if(isObject(data[key]) && !Array.isArray(data[key])){
        var result = clean(data[key], output);
        updateVal(output, key, result);
      }else if(Array.isArray(data[key])){
        var new_arr = [];
        data[key].forEach(function(a_item){
           var a_result = clean(a_item, output);
           if (!isFalsy(a_result)){
                new_arr.push(a_item);
           }
        });
        updateVal(output, key, new_arr);
      }else{
          updateVal(output, key, data[key]);
       }
    });
    return output;
  })(data, {});
}

function updateVal(output,key, val){
    if(!isFalsy(val)){
    output[key] = val;
  }
}

function isObject(data){
    return typeof data === "object" && data !== null;
}

function isFalsy(val){
    return ['', undefined, null].indexOf(val) !== -1 ? 
        true:
        (()=>{
            return typeof(val) === "object" && Object.keys(val).length === 0 ? true: false;
        })();
}

console.log(clean(data));


0 commentaires