Рамда: Как минимизировать вычислительные ресурсы за счет каррирования, фильтрации и отображения? - PullRequest
3 голосов
/ 12 марта 2019

Я создаю приложение React с использованием Ramda. Я все еще новичок в функциональном программировании (~ два месяца).

У меня есть список таких контактов:

const contacts = [
  {
    id: 1,
    firstName: 'Sven',
    lastName: 'Hillstedt',
    city: 'Aachen',
    company: '',
    position: 'Student',
    group: 'friends',
    tendency: 'maintain'
  },
  {
    id: 2,
    firstName: 'David',
  // ...
];

Учитывая строку, мне нужно отфильтровать этот (очень длинный, 10.000-100.000) список. Но мне нужно учитывать только ключи firstName, lastName, city, company и position. Существует массив, содержащий эти:

const FIRST_NAME = 'firstName';
const LAST_NAME = 'lastName';
const CITY = 'city';
const COMPANY = 'company';
const POSITION = 'position';

export const stringFields = [FIRST_NAME, LAST_NAME, CITY, COMPANY, POSITION];

Теперь, используя Рамду, я написал следующую функцию (и), которая принимает string и список контактов, сопоставляет их по клавишам контактов, выбирает соответствующие и опускает их в нижнем регистре, а затем возвращает отфильтрованные контакты:

import { any, filter, includes, map, pick, pipe, toLower, values } from 'ramda';

const contactIncludesValue = value =>
  pipe(
    pick(stringFields),
    map(toLower),
    values,
    any(includes(value))
  );

const filterContactsByValue = value => filter(contactIncludesValue(value));

Как вы можете видеть, этот код грязный (даже если подумать, что он намного красивее, чем делать это обязательно). Я карри value => много раз, что кажется неоптимальным. Я также сомневаюсь, что этот код перебирает контакты только один раз и эффективен ли он.

Как бы вы фильтровали и отображали (выбирали только соответствующие ключи + lowerCase) большой список контактов, не повторяя его дважды или более? Есть ли способ избежать моего карри и написать этот очиститель?

Ответы [ 3 ]

3 голосов
/ 12 марта 2019

Здесь необходимо ответить на несколько вопросов.

  • Даже если комментарии были немного странными, @zerkms это правильно. Нет смысла пытаться оптимизировать производительность, если вы не знаете, что код на самом деле имеет низкую производительность, особенно если это затрудняет написание или сопровождение кода.

  • Вы не карри value => несколько раз. Он каррируется только заранее, и частичное применение вашего значения происходит один раз за фильтрацию списка.

  • Вы перебираете свои контакты только один раз. Но внутри каждого есть вызов any над вашим списком полей. Этот метод возвращает досрочно, если находит совпадение, поэтому рассчитать количество вызовов нетривиально, но, вероятно, O(m * n), где m - это количество полей и n - количество контактов.

Эта версия вашего кода немного более сжатая. Вы можете или не можете найти его более читабельным:

const contactIncludesValue = value =>
  pipe(
    props(stringFields),
    map(toLower),
    any(includes(value))
  );

const filterContactsByValue = pipe(contactIncludesValue, filter);

Обратите внимание, что props удобнее, чем pick(...) -> values, и промежуточный map(toLower) впоследствии будет работать так же хорошо.

2 голосов
/ 12 марта 2019

Как бы вы фильтровали и отображали (выбирали только соответствующие ключи + нижний регистр) большой список контактов, не повторяя его дважды или более?Есть ли способ избежать моего карри и написать этот очиститель?

Если вам нужно отфильтровать И преобразовать ваши данные за один раз, я не понимаю, как вы могли бы сделатьпри этом используется только filter.

Например, это не сохранит a и не преобразует его:

const list = [
  {a: 'foo'},
  {b: 'bar'}
];

console.log(

  filter(pipe(map(toUpper), has('a')), list)

);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {filter, pipe, map, toUpper, has} = R;</script>

Для этого вам нужно либо использовать reduce, либо преобразователь.

Вот решение с использованием преобразователя.В этом примере:

  1. Работать только с объектами, свойство a которых равно 1.
  2. Свойство b, добавить 10
  3. Затем выберите b

const list = [
  {a: 1, b: 2},
  {a: 2, b: 20},
  {a: 1, b: 3},
  {a: 2, b: 30},
  {a: 1, b: 4},
  {a: 2, b: 40},
];

console.log(

into([],
  compose(
    filter(propEq('a', 1)),
    map(over(lensProp('b'), add(10))),
    map(pick(['b']))
  ),
  list)
  
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {into, compose, filter, propEq, map, pick, over, lensProp, add} = R;</script>

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

into([]) говорит Рамде, что вы создаете массив, и поэтому все, что выходит из вашей цепочки compose, должно быть добавлено к нему.

into('') говорит Рамде, что вы производитестрока.Ваша цепочка compose должна возвращать только строку.into позаботится о его соединении до конечного результата:

const list = [
  {a: 1, b: 2},
  {a: 2, b: 20},
  {a: 1, b: 3},
  {a: 2, b: 30},
  {a: 1, b: 4},
  {a: 2, b: 40},
];

console.log(

into('',
  compose(
    filter(propEq('a', 1)),
    map(over(lensProp('b'), add(10))),
    map(prop('b'))
  ),
  list)
  
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {into, compose, filter, propEq, map, over, lensProp, add, prop} = R;</script>
1 голос
/ 21 марта 2019

R.innerJoin наверняка представлял бы самый лаконичный способ его написания, но я не уверен насчет его временной сложности.

const filter = value => R.innerJoin(
  // you may lowercase, etc... here
  (record, prop) => R.propEq(prop, value, record),
  R.__,
  ['firstName', 'lastName', 'city', 'company', 'position'],
);

const onlySven = filter('Sven');
const onlyGiuseppe = filter('Giuseppe');

const data = [
  {
    id: 1,
    firstName: 'Sven',
    lastName: 'Hillstedt',
    city: 'Aachen',
    company: '',
    position: 'Student',
    group: 'friends',
    tendency: 'maintain'
  },
  // ...
];

console.log('Giuseppe', onlyGiuseppe(data));
console.log('Sven', onlySven(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...