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

151 lines
4.2 KiB
JavaScript

(() => {
'use strict';
// GENERIC FUNCTIONS
// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = (f, xs) => [].concat.apply([], xs.map(f));
// deleteFirst :: a -> [a] -> [a]
const deleteFirst = (x, xs) =>
xs.length > 0 ? (
x === xs[0] ? (
xs.slice(1)
) : [xs[0]].concat(deleteFirst(x, xs.slice(1)))
) : [];
// flatten :: Tree a -> [a]
const flatten = t => (t instanceof Array ? concatMap(flatten, t) : [t]);
// unwords :: [String] -> String
const unwords = xs => xs.join(' ');
// words :: String -> [String]
const words = s => s.split(/\s+/);
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = (f, xs, ys) => {
const ny = ys.length;
return (xs.length <= ny ? xs : xs.slice(0, ny))
.map((x, i) => f(x, ys[i]));
};
//------------------------------------------------------------------------
// ORDER DISJOINT LIST ITEMS
// disjointOrder :: [String] -> [String] -> [String]
const disjointOrder = (ms, ns) =>
flatten(
zipWith(
(a, b) => a.concat(b),
segments(ms, ns),
ns.concat('')
)
);
// segments :: [String] -> [String] -> [String]
const segments = (ms, ns) => {
const dct = ms.reduce((a, x) => {
const wds = a.words,
blnFound = wds.indexOf(x) !== -1;
return {
parts: a.parts.concat(blnFound ? [a.current] : []),
current: blnFound ? [] : a.current.concat(x),
words: blnFound ? deleteFirst(x, wds) : wds,
};
}, {
words: ns,
parts: [],
current: []
});
return dct.parts.concat([dct.current]);
};
// -----------------------------------------------------------------------
// FORMATTING TEST OUTPUT
// transpose :: [[a]] -> [[a]]
const transpose = xs =>
xs[0].map((_, iCol) => xs.map((row) => row[iCol]));
// maximumBy :: (a -> a -> Ordering) -> [a] -> a
const maximumBy = (f, xs) =>
xs.reduce((a, x) => a === undefined ? x : (
f(x, a) > 0 ? x : a
), undefined);
// 2 or more arguments
// curry :: Function -> Function
const curry = (f, ...args) => {
const intArgs = f.length,
go = xs =>
xs.length >= intArgs ? (
f.apply(null, xs)
) : function () {
return go(xs.concat([].slice.apply(arguments)));
};
return go([].slice.call(args, 1));
};
// justifyLeft :: Int -> Char -> Text -> Text
const justifyLeft = (n, cFiller, strText) =>
n > strText.length ? (
(strText + replicateS(n, cFiller))
.substr(0, n)
) : strText;
// replicateS :: Int -> String -> String
const replicateS = (n, s) => {
let v = s,
o = '';
if (n < 1) return o;
while (n > 1) {
if (n & 1) o = o.concat(v);
n >>= 1;
v = v.concat(v);
}
return o.concat(v);
};
// -----------------------------------------------------------------------
// TEST
return 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 width = maximumBy((a, b) => a.length > b.length, col)
.length;
return col.map(curry(justifyLeft)(width, ' '));
}))
.map(
([a, b, c]) => a + ' -> ' + b + ' -> ' + c
)
.join('\n');
})();