179 lines
4.5 KiB
JavaScript
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();
|
|
})();
|