var 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
},
...
}
Я хочу отфильтровать все ("isActive" === 0)
, но сохранить тот же набор ключей (который равен идентификатору пользователя) при возврате newObj:
newObj = {
111: {
user_id: 111,
user_name: "user111",
isActive: 0
},
113: {
user_id: 113,
user_name: "use113",
isActive: 0
},
...
}
Это то, что у меня есть сейчас:
let newObj = Object.values(obj).filter( user => ( (obj.isActive === 0)));
который возвращает индексированные ключи
.forEach()
не является обязательным).Настоящим способом FP было бы reduce
с повторяющимся разбросом объектов:
const filtered = Object.values(obj).reduce((p, e) => (!e.isActive ? {...p, [e.user_id]: e} : p), {});
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.info(filtered);
.as-console-wrapper {
max-height: 100% !important;
}
Это создает много ненужных временных объектов, но придерживается принципов FP (я думаю, что я не «глубоко» в FP :-)), не изменяя объекты на месте.
Немного отступив от правил, мы могли бы изменить один объект, а не создавать множество временных объектов:
const filtered = Object.values(obj).reduce((newObj, e) => {
if (!e.isActive) {
newObj[e.user_id] = e;
}
return newObj;
}, {});
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.info(filtered);
.as-console-wrapper {
max-height: 100% !important;
}
(Это можно записать меньшим количеством символов, злоупотребив оператором запятой, но это менее удобно и труднее читать.)
Без ограничения FP я бы просто использовал цикл:
const filtered = {};
for (const e of Object.values(obj)) {
if (!e.isActive) {
filtered[e.user_id] = e;
}
}
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.info(filtered);
.as-console-wrapper {
max-height: 100% !important;
}
Вы можете получать записи, фильтровать и создавать новые объекты.
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.info(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Будет ли это «лучшей практикой», чем использование .filter/.map/.reduce ?
@RickSanchez, на мой взгляд, да, но для других это может быть иначе.
Я бы использовал функцию генератора, которая принимает итерацию:
// 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.info(
itFoldStrict(acc => ([k, v]) => (acc[k] = v, acc))
({}) (ix));
Таким образом, алгоритм
Хорошая идея, к сожалению, ОП хочет вернуть объект, а не массив.
Я намеренно придираюсь, но склонен считать, что objValues
и itFilter
действительно являются промежуточными значениями. Интересное решение однако
@georg Я не хотел делать всю работу для ОП. В любом случае, раз уж ты настоял...
@Kaddath Да, эти функции генератора производят промежуточные значения, но просто крошечные объекты итератора по одному. Алгоритм не строит целую Array
структуру.
«Официально» предлагаемый способ выполнения подобных преобразований объектов заключается в «линеаризации» объекта с помощью Object.entries
, выполнении отображения/фильтрации пар ключ-значение и их обратном объединении с помощью Object.fromEntries
. Последний является новым, поэтому вам понадобится полифилл.
Пример:
// 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.info(result)
Поскольку вы запросили решение FP, вот одно возможное обобщение:
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.info(removeInactive(myObj))
FP предназначен для выражения вашей программы с точки зрения композиции функций, а не для написания циклов «наизнанку» с помощью сокращения.
Я не могу быть разборчивым в ответе и не в другом, я потеряю свою честь! Я полностью согласен с вашим последним комментарием, но разве использование filter
не способ написать цикл, не признаваясь в этом?
@Kaddath: дело в том, что если вы возьмете кусок процедурного кода и завернете его в сокращение, это не сделает вашу программу «функциональной».
Хороший момент, я буду иметь это в виду, полностью добавляет к моей святой борьбе с ветряными мельницами множество «бесполезных» применений FP. Мало того, что это влияет на производительность и удобочитаемость, но в большинстве примеров, которые я видел, тогда это даже не функционировало должным образом!
Следующее также отлично работает:
var newObj = Object.entries(obj).filter(value => {return value[1].isActive ===0});
ИМХО петлевая версия гораздо более читабельна и, вероятно, на сегодняшний день самая производительная, иногда я задаюсь вопросом, не являются ли требования FP только для того, чтобы выглядеть умно (когда не просто ложные предположения о простоте и производительности, где в большинстве случаев FP отстает от обычного петли)