RosettaCodeData/Task/Order-disjoint-list-items/JavaScript/order-disjoint-list-items.js

179 lines
4.5 KiB
JavaScript

(() => {
"use strict";
// ------------ ORDER DISJOINT LIST ITEMS ------------
// disjointOrder :: [String] -> [String] -> [String]
const disjointOrder = ms =>
ns => zipWith(
a => b => [...a, b]
)(
segments(ms)(ns)
)(
ns.concat("")
)
.flat();
// segments :: [String] -> [String] -> [String]
const segments = ms =>
ns => {
const dct = ms.reduce((a, x) => {
const
wds = a.words,
found = wds.indexOf(x) !== -1;
return {
parts: [
...a.parts,
...(found ? [a.current] : [])
],
current: found ? [] : [...a.current, x],
words: found ? deleteFirst(x)(wds) : wds
};
}, {
words: ns,
parts: [],
current: []
});
return [...dct.parts, dct.current];
};
// ---------------------- TEST -----------------------
const main = () =>
transpose(transpose([{
M: "the cat sat on the mat",
N: "mat cat"
}, {
M: "the cat sat on the mat",
N: "cat mat"
}, {
M: "A B C A B C A B C",
N: "C A C A"
}, {
M: "A B C A B D A B E",
N: "E A D A"
}, {
M: "A B",
N: "B"
}, {
M: "A B",
N: "B A"
}, {
M: "A B B A",
N: "B A"
}].map(dct => [
dct.M, dct.N,
unwords(
disjointOrder(
words(dct.M)
)(
words(dct.N)
)
)
]))
.map(col => {
const
w = maximumBy(
comparing(x => x.length)
)(col).length;
return col.map(justifyLeft(w)(" "));
}))
.map(
([a, b, c]) => `${a} -> ${b} -> ${c}`
)
.join("\n");
// ---------------- GENERIC FUNCTIONS ----------------
// comparing :: (a -> b) -> (a -> a -> Ordering)
const comparing = f =>
// The ordering of f(x) and f(y) as a value
// drawn from {-1, 0, 1}, representing {LT, EQ, GT}.
x => y => {
const
a = f(x),
b = f(y);
return a < b ? -1 : (a > b ? 1 : 0);
};
// deleteFirst :: a -> [a] -> [a]
const deleteFirst = x => {
const go = xs => Boolean(xs.length) ? (
x === xs[0] ? (
xs.slice(1)
) : [xs[0]].concat(go(xs.slice(1)))
) : [];
return go;
};
// unwords :: [String] -> String
const unwords = xs =>
// A space-separated string derived
// from a list of words.
xs.join(" ");
// words :: String -> [String]
const words = s =>
// List of space-delimited sub-strings.
s.split(/\s+/u);
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = f =>
// A list constructed by zipping with a
// custom function, rather than with the
// default tuple constructor.
xs => ys => xs.map(
(x, i) => f(x)(ys[i])
).slice(
0, Math.min(xs.length, ys.length)
);
// ---------------- FORMATTING OUTPUT ----------------
// justifyLeft :: Int -> Char -> String -> String
const justifyLeft = n =>
// The string s, followed by enough padding (with
// the character c) to reach the string length n.
c => s => n > s.length ? (
s.padEnd(n, c)
) : s;
// maximumBy :: (a -> a -> Ordering) -> [a] -> a
const maximumBy = f =>
xs => Boolean(xs.length) ? (
xs.slice(1).reduce(
(a, x) => 0 < f(x)(a) ? (
x
) : a,
xs[0]
)
) : undefined;
// transpose :: [[a]] -> [[a]]
const transpose = rows =>
// The columns of a matrix of consistent row length,
// transposed into corresponding rows.
Boolean(rows.length) ? rows[0].map(
(_, i) => rows.flatMap(
v => v[i]
)
) : [];
// MAIN ---
return main();
})();