4
votes

Comment puis-je `.filter ()` un tableau / objet et retourner un nouveau tableau avec les clés d'origine et non comme un tableau / objet indexé comme retour de filtre?

let newObj = Object.values(obj).filter( user => ( (obj.isActive === 0)));

0 commentaires

5 Réponses :


6
votes

La manière vraiment FP serait de réduire avec une répartition répétée des objets:

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

const obj = {
    111: {
        user_id: 111,
        user_name: "user111",
        isActive: 0
    }, 
    112: {
        user_id: 112,
        user_name: "use112",
        isActive: 1
    }, 
    113: {
        user_id: 113,
        user_name: "use113",
        isActive: 0
    }
};
const filtered = {};
for (const e of Object.values(obj)) {
  if (!e.isActive) {
    filtered[e.user_id] = e;
  }
}
console.log(filtered);
const filtered = {};
for (const e of Object.values(obj)) {
  if (!e.isActive) {
    filtered[e.user_id] = e;
  }
}

Cela crée beaucoup d'objets temporaires inutiles, mais adhère aux principes de FP (je pense, je ne suis pas "profond" sur FP :-)) en ne modifiant pas les objets en place.

En pliant un peu les règles, nous pourrions modifier un seul objet plutôt que de créer beaucoup de temporaires:

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

const obj = {
    111: {
        user_id: 111,
        user_name: "user111",
        isActive: 0
    }, 
    112: {
        user_id: 112,
        user_name: "use112",
        isActive: 1
    }, 
    113: {
        user_id: 113,
        user_name: "use113",
        isActive: 0
    }
};
const filtered = Object.values(obj).reduce((newObj, e) => {
  if (!e.isActive) {
    newObj[e.user_id] = e;
  }
  return newObj;
}, {});
console.log(filtered);
const filtered = Object.values(obj).reduce((newObj, e) => {
  if (!e.isActive) {
    newObj[e.user_id] = e;
  }
  return newObj;
}, {});

(Cela peut être écrit en moins de caractères en abusant de l'opérateur virgule, mais c'est moins maintenable et plus difficile à lire.)

Sans la restriction FP, j'utiliserais juste une boucle :

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

const obj = {
    111: {
        user_id: 111,
        user_name: "user111",
        isActive: 0
    }, 
    112: {
        user_id: 112,
        user_name: "use112",
        isActive: 1
    }, 
    113: {
        user_id: 113,
        user_name: "use113",
        isActive: 0
    }
};
const filtered = Object.values(obj).reduce((p, e) => (!e.isActive ? {...p, [e.user_id]: e} : p), {});
console.log(filtered);
const filtered = Object.values(obj).reduce((p, e) => (!e.isActive ? {...p, [e.user_id]: e} : p), {});


1 commentaires

À mon humble avis, la version en boucle est beaucoup plus lisible, et probablement de loin la plus performante, parfois je me demande si les exigences de FP ne sont pas seulement pour avoir l'air intelligent (quand il ne s'agit pas simplement de fausses hypothèses sur la simplicité et la performance, où dans la plupart des cas, FP prend du retard boucles)



1
votes

Vous pouvez obtenir les entrées, filtrer et créer de nouveaux objets.

.as-console-wrapper { max-height: 100% !important; top: 0; }
var object = { 111: { user_id: 111, user_name: "user111", isActive: 0 }, 112: { user_id: 112, user_name: "use112", isActive: 1 }, 113: { user_id: 113, user_name: "use113", isActive: 0 } },
    result = Object.assign(...Object
        .entries(object)
        .filter(({ 1: { isActive } }) => isActive === 0)
        .map(([k, v]) => ({ [k]: v }))
    );

console.log(result);


2 commentaires

Serait-ce une "meilleure pratique" que d'utiliser .filter / .map / .reduce?


@RickSanchez, à mes yeux oui, mais pour d'autres, cela pourrait être différent.



1
votes

J'utiliserais une fonction de générateur qui prend un itérable:

// make object type iterable

function* objEntries(o) {
  for (let k in o)
    yield [k, o[k]];
}

// generator function that takes an iterable

const itFilter = p => function* (ix) {
  for (const x of ix)
    if (p(x))
      yield x;
};

const obj = {
    111: {
        user_id: 111,
        user_name: "user111",
        isActive: 0
    }, 
    112: {
        user_id: 112,
        user_name: "use112",
        isActive: 1
    }, 
    113: {
        user_id: 113,
        user_name: "use113",
        isActive: 0
    }
};

// exhaust the iterator with a strict evaluated fold

const itFoldStrict = f => acc => ix => {
  let acc_ = acc;

  for (const x of ix)
    acc_ = f(acc_) (x);

  return acc_;
};

const ix = itFilter(([k, o]) => o.isActive === 0)
  (objEntries(obj));

// nothin has happened here due to lazy evaluation

// unleash the effect (of constructing the filtered object)

console.log(
  itFoldStrict(acc => ([k, v]) => (acc[k] = v, acc))
    ({}) (ix));

De cette façon, l'algorithme

  • est paresseux
  • ne produit pas de valeurs intermédiaires


4 commentaires

Bonne idée, malheureusement, l'OP veut récupérer un objet, pas un tableau.


Je suis pointilleux ici exprès, mais j'aurais tendance à considérer que objValues ​​ et itFilter sont en effet des valeurs intermédiaires. Solution intéressante cependant


@georg Je ne voulais pas faire tout le travail pour l'OP. Bref, puisque tu as insisté ...


@Kaddath Oui, ces fonctions génératrices produisent des valeurs intermédiaires, mais simplement de minuscules objets itérateurs un à la fois. L'algorithme ne construit pas une structure Array entière.



2
votes

La manière "officiellement" suggérée d'effectuer des transformations d'objet comme celle-ci est de "linéariser" l'objet avec Object.entries , effectuez une mappe / filtre sur des paires clé-valeur et remettez-les ensemble avec Object.fromEntries . Ce dernier est nouveau, vous aurez donc besoin d'un polyfill.

Exemple:

let apply = (x, fn) => fn(x);

let pipe = (...fns) => x => fns.reduce(apply, x);

let transform = fn => pipe(Object.entries, fn, Object.fromEntries);

let filter = fn => a => a.filter(fn);

let filterObject = fn => transform(filter(fn));

let removeInactive = filterObject(([k, v]) => v.isActive);

console.log(removeInactive(myObj))

Puisque vous avez demandé une solution FP, voici une généralisation possible:

// polyfill

Object.fromEntries = Object.fromEntries || function(pairs) {
    let obj = {};
    for (let [k, v] of pairs)
        obj[k] = v;
    return obj;
};


var myObj = {

    111: {
        user_id: 111,
        user_name: "user111",
        isActive: 0
    },
    112: {
        user_id: 112,
        user_name: "use112",
        isActive: 1
    },
    113: {
        user_id: 113,
        user_name: "use113",
        isActive: 0
    },
};

result = Object.fromEntries(
    Object.entries(myObj)
        .filter(([k, v]) => v.isActive));

console.log(result)

FP consiste à exprimer votre programme en termes de composition de fonction, pas sur l'écriture de boucles "à l'envers" avec réduire.


3 commentaires

Je ne peux pas être pointilleux sur une réponse et pas sur une autre, je perdrais mon honneur! Je suis tout à fait d'accord avec votre dernier commentaire, mais l'utilisation de filter n'est-elle pas un moyen d'écrire une boucle sans l'admettre?


@Kaddath: le fait est que si vous prenez un morceau de code procédural et que vous l'enveloppez dans Reduce, cela ne rend pas votre programme "fonctionnel".


Bon point, je vais garder cela à l'esprit, ajoute totalement à mon combat sacré contre les moulins à vent des nombreuses utilisations "inutiles" de la PF. Non seulement cela affecte les performances et la lisibilité, mais dans la plupart des exemples que j'ai vus, il n'était même pas correctement fonctionnel à l'époque!



0
votes

Ce qui suit fonctionne bien aussi:

var newObj = Object.entries(obj).filter(value => {return value[1].isActive ===0});


0 commentaires