Используйте array.reduce для соединения строк с запятой, но избегайте лишней запятой - PullRequest
1 голос
/ 25 мая 2020

У меня есть массив объектов, которые нужно преобразовать и объединить в одну строку с array.reduce.

const arr = [{id: 7, task: "foo"}, {id: 22, task: "bar"}]

Результат должен быть 7. foo, 22. bar

Если я напишу этот код , он будет работать, но выдаст , 7. foo, 22.bar:

arr.reduce((pre,cur)=> pre + `, ${cur.id}. ${cur.task}`, '')

Как мне правильно сделать это без лишней запятой, желательно только в FP?

Ответы [ 5 ]

9 голосов
/ 25 мая 2020

Требуется ли сокращение? Карту легче понять и прочитать.

arr.map(o => `${o.id}. ${o.task}`).join(',')
1 голос
/ 26 мая 2020

функциональный 101

Идиоматическое решение c JavaScript: map - join. Но функциональное программирование - это разбиение программы на повторно используемые модули и создание барьеров для абстракции -

// Task.js

const empty =
  { id: 0, task: "" }

const task = (id = 0, task = "") =>
  ({ id, task })

const toString = (t = empty) =>
  `${t.id}. ${t.task}`

const toStringAll = ([ first, ...rest ]) =>
  rest.reduce   // <-- reduce
    ( (r, x) => r + ", " + toString(x)  
    , toString(first) 
    )

export { empty, task, toString, toStringAll } 

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

Теперь мы закрываем воображаемую крышку на нашем модуле и забываем всю сложность внутри. Остается чистый интерфейс, который четко передает возможности модуля -

// Main.js

import { task, toStringAll } from './Task'

const data =
  [ task(7, "foo")
  , task(22, "bar")
  , task(33, "qux")
  ]

console.log(toStringAll(data))
// 7. foo, 22. bar, 33. qux

console.log(toStringAll(data.slice(0,2)))
// 7. foo, 22. bar

console.log(toStringAll(data.slice(0,1)))
// 7. foo

console.log(toStringAll(data.slice(0,0)))
// 0.

Разверните приведенный ниже фрагмент, чтобы проверить результат в своем браузере -

const empty =
  { id: 0, task: "" }
  
const task = (id = 0, task = "") =>
  ({ id, task })
  
const toString = (t = empty) =>
  `${t.id}. ${t.task}`
  
const toStringAll = ([ first, ...rest ]) =>
  rest.reduce
    ( (r, x) => r + ", " + toString(x)
    , toString(first) 
    )
    
const data =
  [ task(7, "foo")
  , task(22, "bar")
  , task(33, "qux")
  ]

console.log(toStringAll(data))
// 7. foo, 22. bar, 33. qux

console.log(toStringAll(data.slice(0,2)))
// 7. foo, 22. bar

console.log(toStringAll(data.slice(0,1)))
// 7. foo

console.log(toStringAll(data.slice(0,0)))
// 0.

барьер абстракции

Main.js отделен от Task барьером абстракции. Теперь мы можем выбрать любую реализацию для toString и toStringAll, не требуя от Main заботы о том, как Task работает под крышкой.

Попрактикуемся в внесении изменений и обновим, как empty задача представлена. В приведенной выше программе мы видим 0., но вместо этого мы заставим его сказать Empty.. И просто для удовольствия, давайте попробуем новую реализацию toStringAll -

// Task.js

const empty = //

const task = //

const toString = (t = empty) =>
  t === empty
    ? `Empty.`                // <-- custom representation for empty
    : `${t.id}. ${t.task}`

const toStringAll = ([ t = empty, ...more ]) =>
  more.length === 0
    ? toString(t)
    : toString(t) + ", " + toStringAll(more) // <-- recursive

//

export { empty, task, toString, toStringAll }

Main, не нужно делать ничего по-другому -

// Main.js

import { task, toStringAll } from './Task'

const data = //

console.log(toStringAll(data))
// 7. foo, 22. bar, 33. qux

console.log(toStringAll(data.slice(0,2)))
// 7. foo, 22. bar

console.log(toStringAll(data.slice(0,1)))
// 7. foo

console.log(toStringAll(data.slice(0,0)))
// Empty.

Разверните фрагмент ниже, чтобы проверьте результат в своем браузере -

const empty =
  { id: 0, task: "" }
  
const task = (id = 0, task = "") =>
  ({ id, task })
  
const toString = (t = empty) =>
  t === empty
    ? `Empty.`
    : `${t.id}. ${t.task}`
  
const toStringAll = ([ t = empty, ...more ]) =>
  more.length === 0
    ? toString(t)
    : toString(t) + ", " + toStringAll(more)
    
const data =
  [ task(7, "foo")
  , task(22, "bar")
  , task(33, "qux")
  ]

console.log(toStringAll(data))
// 7. foo, 22. bar, 33. qux

console.log(toStringAll(data.slice(0,2)))
// 7. foo, 22. bar

console.log(toStringAll(data.slice(0,1)))
// 7. foo

console.log(toStringAll(data.slice(0,0)))
// Empty.
1 голос
/ 25 мая 2020

Вы можете легко решить эту проблему, проверив, не является ли pre ложным значением, используя тернарный оператор в начале, например:

`${pre ? ', ' : ''}`

const arr = [{id: 7, task: "foo"}, {id: 22, task: "bar"}]
const res = arr.reduce((pre,cur)=> pre + `${pre ? ', ' : ''}${cur.id}. ${cur.task}`, '')
console.log(res)
0 голосов
/ 25 мая 2020

Вы можете использовать аргумент index редуктора:

const format = object => `${object.id}. ${object.task}`;
const result = arr.reduce((pre, object, index) => (index ? pre + ', ' : '') + format(object), '');
0 голосов
/ 25 мая 2020

вы можете добавить проверку для первого элемента массива:

arr.reduce((pre, cur, index) => {
  if(index === 0) {
    return `${cur.id}. ${cur.task}`
  }; 
  return pre + `, ${cur.id}. ${cur.task}`}
, '');
...