Один довольно простой метод, если не самый эффективный, - это создать квадратную сетку со строкой для каждого значения, либо всех нулей, либо по убыванию от одного ненулевого значения, как описано, а затем просто добавить столбцы.
Вот моя версия:
const pop = arr => arr.map(
(n, i) => n == 0
? Array(arr.length).fill(0)
: arr.map((_, j) => Math.max(n - Math.abs(j - i), 0))
).reduce((as, bs) => as.map((a, i) => a + bs[i]))
console.log(...pop([0, 0, 2, 0, 0])) //~> [0, 1, 2, 1, 0]
console.log(...pop([3, 0, 0, 0])) //~> [3, 2, 1, 0]
console.log(...pop([0, 0, 0, 3, 0, 2, 0])) //~> [0, 1, 2, 3, 3, 3, 1]
console.log(...pop([0, 0, 0, 4, 0, 0, 0])) //~> [1, 2, 3, 4, 3, 2, 1]
console.log(...pop([0, 0, 0, 0, 4, 0, 0, 3, 0])) //~> [0, 1, 2, 3, 4, 4, 4, 4, 2]
Обратите внимание, что промежуточный результат (после map
, до reduce
) для этого последнего выглядит следующим образом:
[
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 2, 3, 4, 3, 2, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 2, 3, 2],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
]
Оттуда это просто вопрос добавления столбцов, простой вызов reduce
.
Обновление
Комментарий NinaScholz заставил меня немного переосмыслить, и я понял, что это так же легко сделатьначальный reduce
вместо map
и создайте только те массивы, которые необходимы.Это изменение должно быть более эффективным:
const spread = arr => arr.reduce(
(a, n, i) => n == 0
? a
: a.concat([arr.map((_, j) => Math.max(n - Math.abs(j - i), 0))]),
[]
).reduce((as, bs) => as.map((a, i) => a + bs[i]))
console.log(...spread([0, 0, 2, 0, 0])) //~> [0, 1, 2, 1, 0]
console.log(...spread([3, 0, 0, 0])) //~> [3, 2, 1, 0]
console.log(...spread([0, 0, 0, 3, 0, 2, 0])) //~> [0, 1, 2, 3, 3, 3, 1]
console.log(...spread([0, 0, 0, 4, 0, 0, 0])) //~> [1, 2, 3, 4, 3, 2, 1]
console.log(...spread([0, 0, 0, 0, 4, 0, 0, 3, 0])) //~> [0, 1, 2, 3, 4, 4, 4, 4, 2]
С этим изменением промежуточный результат (между двумя reduce
вызовами сейчас) будет состоять только из
[
[0, 1, 2, 3, 4, 3, 2, 1, 0],
[0, 0, 0, 0, 0, 1, 2, 3, 2],
]
Обновление 2
Видя это снова, я понимаю, что последнее изменение не зашло достаточно далеко.Мы можем исключить все промежуточные массивы, кроме одного, используемого как reduce
аккумулятор, просто добавляя к текущему значению по ходу работы.
Вот, что я надеюсь, это моя окончательная версия:
const spread = arr => arr.reduce(
(a, n, i) => n == 0
? a
: arr.map((_, j) => a[j] + Math.max(n - Math.abs(j - i), 0)),
Array(arr.length).fill(0)
)
console.log(...spread([0, 0, 2, 0, 0])) //~> [0, 1, 2, 1, 0]
console.log(...spread([3, 0, 0, 0])) //~> [3, 2, 1, 0]
console.log(...spread([0, 0, 0, 3, 0, 2, 0])) //~> [0, 1, 2, 3, 3, 3, 1]
console.log(...spread([0, 0, 0, 4, 0, 0, 0])) //~> [1, 2, 3, 4, 3, 2, 1]
console.log(...spread([0, 0, 0, 0, 4, 0, 0, 3, 0])) //~> [0, 1, 2, 3, 4, 4, 4, 4, 2
У него нет промежуточных структур данных, кроме этого аккумулятора.Это делает только необходимую арифметику.AFAICT, это настолько эффективно, насколько это возможно, по модулю работы с reduce
, а не примитивным for
-loop.Хотелось бы знать, если я что-то упустил.