Как мне `.filter()` массив/объект и вернуть новый массив с исходными ключами, а не как индексированный массив/объект в качестве возврата фильтра?

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)));

который возвращает индексированные ключи

  • нет циклов for (если только ES6 .forEach() не является обязательным).
  • Я бы хотел, чтобы подход ES6 к этой проблеме использовал filter/map/reduce, если это возможно в этой ситуации.
  • Подход loadash в порядке, но я все еще хочу увидеть пример "vanilla ES6", прежде чем
  • Если бы я мог получить совет о том, как/где лучше всего учиться и практиковаться, чтобы фильтровать массивы всеми возможными способами, используя эти методы (здесь тоже будет круто).
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
4
0
175
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Настоящим способом 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;
}

ИМХО петлевая версия гораздо более читабельна и, вероятно, на сегодняшний день самая производительная, иногда я задаюсь вопросом, не являются ли требования FP только для того, чтобы выглядеть умно (когда не просто ложные предположения о простоте и производительности, где в большинстве случаев FP отстает от обычного петли)

Kaddath 10.04.2019 10:30

Вы можете получать записи, фильтровать и создавать новые объекты.

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 ?

W.Doch 10.04.2019 11:11

@RickSanchez, на мой взгляд, да, но для других это может быть иначе.

Nina Scholz 10.04.2019 11:29

Я бы использовал функцию генератора, которая принимает итерацию:

// 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));

Таким образом, алгоритм

  • ленивый
  • не дает промежуточных значений

Хорошая идея, к сожалению, ОП хочет вернуть объект, а не массив.

georg 10.04.2019 10:55

Я намеренно придираюсь, но склонен считать, что objValues и itFilter действительно являются промежуточными значениями. Интересное решение однако

Kaddath 10.04.2019 10:57

@georg Я не хотел делать всю работу для ОП. В любом случае, раз уж ты настоял...

Iven Marquardt 10.04.2019 11:41

@Kaddath Да, эти функции генератора производят промежуточные значения, но просто крошечные объекты итератора по одному. Алгоритм не строит целую Array структуру.

Iven Marquardt 10.04.2019 11:44

«Официально» предлагаемый способ выполнения подобных преобразований объектов заключается в «линеаризации» объекта с помощью 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 10.04.2019 11:07

@Kaddath: дело в том, что если вы возьмете кусок процедурного кода и завернете его в сокращение, это не сделает вашу программу «функциональной».

georg 10.04.2019 11:34

Хороший момент, я буду иметь это в виду, полностью добавляет к моей святой борьбе с ветряными мельницами множество «бесполезных» применений FP. Мало того, что это влияет на производительность и удобочитаемость, но в большинстве примеров, которые я видел, тогда это даже не функционировало должным образом!

Kaddath 10.04.2019 11:41

Следующее также отлично работает:

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

Другие вопросы по теме