Как объединить два массива при отображении? - PullRequest
0 голосов
/ 09 сентября 2018

background : Я довольно новичок в Ramda и FP. Я сталкиваюсь с этим сценарием, где у меня есть два входа itemList и costList. Значения в списке имеют отношение, основанное на idx. Таким образом, itemList[0] и costList[0] представляют значения, которые могут находиться в одном и том же объекте (например, { item: itemList[0], cost: costList[0]}). Таким образом, альтернативное решение может начаться с объединения itemList и costList. Я заинтересован в одном или обоих решениях. Любые другие советы и предложения будут также оценены.

  let itemList = ['shirt', 'shoes'];
  let costList = ['shirtCost', 'shoesCost'];

  let formulaList = R.map(
    x => ({
      formulaXAttr: x,
      formulaYAttr: 'tbd later',
    })
  )(itemList);

  let finalList = R.map(
    x => R.merge(x, {formulaYAttr: '???'})  // how to merge in costList?
  )(formulaList);

  // this is how i'd do it in vanilla JS
  let finalList = formulaList.map((x, idx) => ({
    ...x,
    formulaYAttr: costList[idx],
  }));

Ответы [ 3 ]

0 голосов
/ 10 сентября 2018

Ответ Скотта Кристофера, безусловно, правильный. zipWith предназначен именно для такого сценария. В своем заголовке вы спрашиваете, как это сделать во время картирования. Я хотел бы отметить, что, хотя Рамда действительно предлагает вариант для этого (подробности см. addIndex), он, как правило, осуждается. Причина этого важна для получения дополнительной информации о FP.

Одно понимание map состоит в том, что он преобразует список элементов одного типа в список элементов другого типа посредством применения данной функции к каждому элементу. Это хорошая формулировка, но есть более общая формулировка: map преобразует контейнер элементов одного типа в контейнер элементов другого типа посредством применения данная функция для каждого элемента. Другими словами, понятие mapping может применяться ко многим различным контейнерам, а не только к спискам. Спецификация FantatsyLand определяет некоторые правила для контейнеров, которые имеют методы map. Конкретный тип - Functor, но для тех, у кого нет соответствующего математического фона, это можно рассматривать как Mappable. Рамда должен хорошо взаимодействовать с такими типами:

const square = n => n * n;
map(square, [1, 2, 3, 4, 5])  //=> [1, 4, 9, 16, 25]


// But also, if MyContainer is a Functor
map(square, MyContainer(7)) //=> MyContainer(49)


// and, for example, if for some Maybe Functor with Just and Nothing subtypes
const {Just, Nothing} = {Maybe}
// then
map(square, Just(6)) //=> Just(36)
map(square, Nothing()) //=> Nothing()

Функция Рамды map вызывает предоставленную функцию преобразования только с элементом контейнера. Он не предоставляет индекс. Это имеет смысл, поскольку не все контейнеры имеют какое-либо представление об индексах.

Из-за этого картирование - это то, что Рамда не место для попыток сопоставить индексы. Но это в основе трех zip функций Рамды . Если вы хотите объединить два списка, элементы которых сопоставляются по индексу, вы можете сделать это наиболее просто, как Скотт сделал с zipWith. Но вы также можете использовать zip, который работает так:

zip(['a', 'b', 'c', 'd'], [2, 3, 5, 7]) //=> [['a', 2], ['b', 3], ['c', 5], ['d', 7]]

с последующим вызовом map по полученным парам. zipWith просто позволяет вам сделать то же самое за один шаг. Но без этого примитивы, такие как map и zip, все еще можно комбинировать, чтобы делать то, что вам нравится.

0 голосов
/ 10 сентября 2018

Чтобы согласиться с другими ответами здесь, я хочу показать вам, как писать подобные вещи самостоятельно. Библиотека Ramda не всегда будет иметь решение для одной строки, и вы сдерживаете себя, если ограничиваете свои решения использованием только функций, предоставленных Ramda.

Эта практика придаст вам уверенности в попытках написать собственную программу. Когда / если вы обнаружите, что Ramda обладает некоторыми необходимыми вам функциями, вы можете легко реорганизовать вашу программу для использования функции, предоставленной Ramda.

const None =
  Symbol ()

const zipWith = (f, [ x = None, ...xs ], [ y = None, ...ys ]) =>
  x === None || y === None
    ? []
    : [ f (x, y) ] .concat (zipWith (f, xs, ys))

console.log
  ( zipWith
      ( (name, price) => ({ name, price })
      , [ 'shoes', 'pants' ]
      , [ 19.99, 29.99 ]
      )
  )

// [ { name: "shoes", price: 19.99 }
// , { name: "pants", price: 29.99 }
// ]

Используемое выше деструктурирующее назначение помогает поддерживать декларативный стиль, но создает ненужные промежуточные значения. Ниже мы используем дополнительный параметр состояния i, чтобы значительно уменьшить объем памяти.

const zipWith = (f, xs, ys, i = 0) =>
  i >= xs.length || i >= ys.length
    ? []
    : [ f (xs[i], ys[i]) ] .concat (zipWith (f, xs, ys, i + 1))
    
console.log
  ( zipWith
      ( (name, price) => ({ name, price })
      , [ 'shoes', 'pants' ]
      , [ 19.99, 29.99 ]
      )
  )
  
// [ { name: "shoes", price: 19.99 }
// , { name: "pants", price: 29.99 }
// ]

Обратите внимание, что обе реализации чистые. Там нет никаких побочных эффектов. Входы не видоизменяются, и для вывода всегда создается новый массив.

Наша реализация также является полной, так как она всегда будет отвечать действительным выводом, когда предоставляется действительный ввод.

console.log
  ( zipWith
      ( (name, price) => ({ name, price })
      , [ 'shoes' ]
      , [ 19.99 ]
      )
      // [ { name: "shoes", price: 19.99 } ]

  , zipWith
      ( (name, price) => ({ name, price })
      , [ 'shoes' ]
      , []
      )
      // []

  , zipWith
      ( (name, price) => ({ name, price })
      , []
      , [ 19.99 ]
      )
      // []

  , zipWith
      ( (name, price) => ({ name, price })
      , []
      , []
      )
      // []
  )

На этот раз у Рамды есть твоя спина с R.zipWith, но в следующий раз может и нет. Пока не бойтесь, потому что вы получили это! Написание таких процедур становится легким, когда вы понимаете, что в программировании нет магии. И если вы застряли, я думаю, что всегда есть StackOverflow.

0 голосов
/ 09 сентября 2018

Функция, которую вы ищете в Ramda, называется zipWith, которая принимает функцию, ожидающую два аргумента, по одному для каждого элемента первого списка вместе со своим попарным элементом из второго списка. В результате получается список, длина которого меньше двух предоставленных списков и содержит результаты вызова функции для каждой пары.

const itemList = ['shirt', 'shoes'];
const costList = ['shirtCost', 'shoesCost'];

const fn = R.zipWith((x, y) =>
  ({ formulaXAttr: x, formulaYAttr: y }))

console.log(
  fn(itemList, costList)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
...