122 lines
3.4 KiB
JavaScript
122 lines
3.4 KiB
JavaScript
(() => {
|
|
'use strict';
|
|
|
|
// consolidated :: Ord a => [Set a] -> [Set a]
|
|
const consolidated = xs => {
|
|
const go = (s, xs) =>
|
|
0 !== xs.length ? (() => {
|
|
const h = xs[0];
|
|
return 0 === intersection(h, s).size ? (
|
|
[h].concat(go(s, tail(xs)))
|
|
) : go(union(h, s), tail(xs));
|
|
})() : [s];
|
|
return foldr(go, [], xs);
|
|
};
|
|
|
|
|
|
// TESTS ----------------------------------------------
|
|
const main = () =>
|
|
map(xs => intercalate(
|
|
', and ',
|
|
map(showSet, consolidated(xs))
|
|
),
|
|
map(x => map(
|
|
s => new Set(chars(s)),
|
|
x
|
|
),
|
|
[
|
|
['ab', 'cd'],
|
|
['ab', 'bd'],
|
|
['ab', 'cd', 'db'],
|
|
['hik', 'ab', 'cd', 'db', 'fgh']
|
|
]
|
|
)
|
|
).join('\n');
|
|
|
|
|
|
// GENERIC FUNCTIONS ----------------------------------
|
|
|
|
// chars :: String -> [Char]
|
|
const chars = s => s.split('');
|
|
|
|
// concat :: [[a]] -> [a]
|
|
// concat :: [String] -> String
|
|
const concat = xs =>
|
|
0 < xs.length ? (() => {
|
|
const unit = 'string' !== typeof xs[0] ? (
|
|
[]
|
|
) : '';
|
|
return unit.concat.apply(unit, xs);
|
|
})() : [];
|
|
|
|
// elems :: Dict -> [a]
|
|
// elems :: Set -> [a]
|
|
const elems = x =>
|
|
'Set' !== x.constructor.name ? (
|
|
Object.values(x)
|
|
) : Array.from(x.values());
|
|
|
|
// flip :: (a -> b -> c) -> b -> a -> c
|
|
const flip = f =>
|
|
1 < f.length ? (
|
|
(a, b) => f(b, a)
|
|
) : (x => y => f(y)(x));
|
|
|
|
// Note that that the Haskell signature of foldr differs from that of
|
|
// foldl - the positions of accumulator and current value are reversed
|
|
|
|
// foldr :: (a -> b -> b) -> b -> [a] -> b
|
|
const foldr = (f, a, xs) => xs.reduceRight(flip(f), a);
|
|
|
|
// intercalate :: [a] -> [[a]] -> [a]
|
|
// intercalate :: String -> [String] -> String
|
|
const intercalate = (sep, xs) =>
|
|
0 < xs.length && 'string' === typeof sep &&
|
|
'string' === typeof xs[0] ? (
|
|
xs.join(sep)
|
|
) : concat(intersperse(sep, xs));
|
|
|
|
// intersection :: Ord a => Set a -> Set a -> Set a
|
|
const intersection = (s, s1) =>
|
|
new Set([...s].filter(x => s1.has(x)));
|
|
|
|
// intersperse :: a -> [a] -> [a]
|
|
// intersperse :: Char -> String -> String
|
|
const intersperse = (sep, xs) => {
|
|
const bln = 'string' === typeof xs;
|
|
return xs.length > 1 ? (
|
|
(bln ? concat : x => x)(
|
|
(bln ? (
|
|
xs.split('')
|
|
) : xs)
|
|
.slice(1)
|
|
.reduce((a, x) => a.concat([sep, x]), [xs[0]])
|
|
)) : xs;
|
|
};
|
|
|
|
// map :: (a -> b) -> [a] -> [b]
|
|
const map = (f, xs) => xs.map(f);
|
|
|
|
// showSet :: Set -> String
|
|
const showSet = s =>
|
|
intercalate(elems(s), ['{', '}']);
|
|
|
|
// sort :: Ord a => [a] -> [a]
|
|
const sort = xs => xs.slice()
|
|
.sort((a, b) => a < b ? -1 : (a > b ? 1 : 0));
|
|
|
|
// tail :: [a] -> [a]
|
|
const tail = xs => 0 < xs.length ? xs.slice(1) : [];
|
|
|
|
// union :: Ord a => Set a -> Set a -> Set a
|
|
const union = (s, s1) =>
|
|
Array.from(s1.values())
|
|
.reduce(
|
|
(a, x) => (a.add(x), a),
|
|
new Set(s)
|
|
);
|
|
|
|
// MAIN ---
|
|
return main();
|
|
})();
|