RosettaCodeData/Task/Equilibrium-index/JavaScript/equilibrium-index-5.js

110 lines
2.7 KiB
JavaScript

(() => {
"use strict";
// ------------- ALL EQUILIBRIUM INDICES -------------
// equilibriumIndices :: [Int] -> [Int]
const equilibriumIndices = xs =>
zip(
scanl1(add)(xs)
)(
scanr1(add)(xs)
)
.reduceRight(
(a, xy, i) => xy[0] === xy[1] ? (
[i, ...a]
) : a, []
);
// ---------------------- TEST -----------------------
const main = () => [
[-7, 1, 5, 2, -4, 3, 0],
[2, 4, 6],
[2, 9, 2],
[1, -1, 1, -1, 1, -1, 1],
[1],
[]
].map(compose(
JSON.stringify,
equilibriumIndices
))
.join("\n");
// -> [[3, 6], [], [1], [0, 1, 2, 3, 4, 5, 6], [0], []]
// ---------------- GENERIC FUNCTIONS ----------------
// add (+) :: Num a => a -> a -> a
const add = a =>
// Curried addition.
b => a + b;
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
const compose = (...fs) =>
// A function defined by the right-to-left
// composition of all the functions in fs.
fs.reduce(
(f, g) => x => f(g(x)),
x => x
);
// scanl :: (b -> a -> b) -> b -> [a] -> [b]
const scanl = f => startValue => xs =>
// The series of interim values arising
// from a catamorphism. Parallel to foldl.
xs.reduce((a, x) => {
const v = f(a[0])(x);
return [v, a[1].concat(v)];
}, [startValue, [startValue]])[1];
// scanl1 :: (a -> a -> a) -> [a] -> [a]
const scanl1 = f =>
// scanl1 is a variant of scanl that has no
// starting value argument.
xs => xs.length > 0 ? (
scanl(f)(
xs[0]
)(xs.slice(1))
) : [];
// scanr :: (a -> b -> b) -> b -> [a] -> [b]
const scanr = f =>
startValue => xs => xs.reduceRight(
(a, x) => {
const v = f(x)(a[0]);
return [v, [v].concat(a[1])];
}, [startValue, [startValue]]
)[1];
// scanr1 :: (a -> a -> a) -> [a] -> [a]
const scanr1 = f =>
// scanr1 is a variant of scanr that has no
// seed-value argument, and assumes that
// xs is not empty.
xs => xs.length > 0 ? (
scanr(f)(
xs.slice(-1)[0]
)(xs.slice(0, -1))
) : [];
// zip :: [a] -> [b] -> [(a, b)]
const zip = xs =>
// The paired members of xs and ys, up to
// the length of the shorter of the two lists.
ys => Array.from({
length: Math.min(xs.length, ys.length)
}, (_, i) => [xs[i], ys[i]]);
// MAIN ---
return main();
})();