RosettaCodeData/Task/Cheryls-birthday/JavaScript/cheryls-birthday.js
2023-07-01 13:44:08 -04:00

177 lines
4.9 KiB
JavaScript

(() => {
'use strict';
// main :: IO ()
const main = () => {
const
month = fst,
day = snd;
showLog(
map(x => Array.from(x), (
// The month with only one remaining day,
// (A's month contains only one remaining day)
// (3 :: A "Then I also know")
uniquePairing(month)(
// among the days with unique months,
// (B's day is paired with only one remaining month)
// (2 :: B "I know now")
uniquePairing(day)(
// excluding months with unique days,
// (A's month is not among those with unique days)
// (1 :: A "I know that Bernard does not know")
monthsWithUniqueDays(false)(
// from the given month-day pairs:
// (0 :: Cheryl's list)
map(x => tupleFromList(words(strip(x))),
splitOn(/,\s+/,
`May 15, May 16, May 19,
June 17, June 18, July 14, July 16,
Aug 14, Aug 15, Aug 17`
)
)
)
)
)
))
);
};
// monthsWithUniqueDays :: Bool -> [(Month, Day)] -> [(Month, Day)]
const monthsWithUniqueDays = blnInclude => xs => {
const months = map(fst, uniquePairing(snd)(xs));
return filter(
md => (blnInclude ? id : not)(
elem(fst(md), months)
),
xs
);
};
// uniquePairing :: ((a, a) -> a) ->
// -> [(Month, Day)] -> [(Month, Day)]
const uniquePairing = f => xs =>
bindPairs(xs,
md => {
const
dct = f(md),
matches = filter(
k => 1 === length(dct[k]),
Object.keys(dct)
);
return filter(tpl => elem(f(tpl), matches), xs);
}
);
// bindPairs :: [(Month, Day)] -> (Dict, Dict) -> [(Month, Day)]
const bindPairs = (xs, f) => f(
Tuple(
dictFromPairs(fst)(snd)(xs),
dictFromPairs(snd)(fst)(xs)
)
);
// dictFromPairs :: ((a, a) -> a) -> ((a, a) -> a) -> [(a, a)] -> Dict
const dictFromPairs = f => g => xs =>
foldl((a, tpl) => Object.assign(
a, {
[f(tpl)]: (a[f(tpl)] || []).concat(g(tpl).toString())
}
), {}, xs);
// GENERIC ABSTRACTIONS -------------------------------
// Tuple (,) :: a -> b -> (a, b)
const Tuple = (a, b) => ({
type: 'Tuple',
'0': a,
'1': b,
length: 2
});
// elem :: Eq a => a -> [a] -> Bool
const elem = (x, xs) => xs.includes(x);
// filter :: (a -> Bool) -> [a] -> [a]
const filter = (f, xs) => xs.filter(f);
// foldl :: (a -> b -> a) -> a -> [b] -> a
const foldl = (f, a, xs) => xs.reduce(f, a);
// fst :: (a, b) -> a
const fst = tpl => tpl[0];
// id :: a -> a
const id = x => x;
// intersect :: (Eq a) => [a] -> [a] -> [a]
const intersect = (xs, ys) =>
xs.filter(x => -1 !== ys.indexOf(x));
// Returns Infinity over objects without finite length
// this enables zip and zipWith to choose the shorter
// argument when one is non-finite, like cycle, repeat etc
// length :: [a] -> Int
const length = xs =>
(Array.isArray(xs) || 'string' === typeof xs) ? (
xs.length
) : Infinity;
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
// not :: Bool -> Bool
const not = b => !b;
// showLog :: a -> IO ()
const showLog = (...args) =>
console.log(
args
.map(JSON.stringify)
.join(' -> ')
);
// snd :: (a, b) -> b
const snd = tpl => tpl[1];
// splitOn :: String -> String -> [String]
const splitOn = (pat, src) =>
src.split(pat);
// strip :: String -> String
const strip = s => s.trim();
// tupleFromList :: [a] -> (a, a ...)
const tupleFromList = xs =>
TupleN.apply(null, xs);
// TupleN :: a -> b ... -> (a, b ... )
function TupleN() {
const
args = Array.from(arguments),
lng = args.length;
return lng > 1 ? Object.assign(
args.reduce((a, x, i) => Object.assign(a, {
[i]: x
}), {
type: 'Tuple' + (2 < lng ? lng.toString() : ''),
length: lng
})
) : args[0];
};
// words :: String -> [String]
const words = s => s.split(/\s+/);
// MAIN ---
return main();
})();