Cheryl's birthday: Difference between revisions

Content added Content deleted
(=={{header|JavaScript}}== Added a first JS draft, aiming for some degree of legibility)
Line 167: Line 167:
Cheryl's birthday is July 16
Cheryl's birthday is July 16
</pre>
</pre>

=={{header|JavaScript}}==
<lang javascript>(() => {
'use strict';

// main :: IO ()
const main = () =>
showLog(
map(x => Array.from(x), (

// The month with only one remaining day,
monthsWithSingleDays(true)(

// among the days with unique months,
daysWithUniqueMonths(true)(

// excluding months with unique days,
monthsWithUniqueDays(false)(

// from the given month-day pairs:
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
dctDays = dictFromPairs(snd)(fst)(xs),
dctMonths = dictFromPairs(fst)(snd)(xs),
days = filter(
k => 1 === length(dctDays[k]),
Object.keys(dctDays)
),
months = filter(
k => (blnInclude ? id : not)(
0 < length(intersect(dctMonths[k], days))
),
Object.keys(dctMonths)
);
return filter(tpl => elem(fst(tpl), months), xs);
};

// daysWithUniqueMonths :: Bool -> [(Month, Day)] -> [(Month, Day)]
const daysWithUniqueMonths = blnInclude => xs => {
const
dctDays = dictFromPairs(snd)(fst)(xs),
dctMonths = dictFromPairs(fst)(snd)(xs),
days = filter(
k => (blnInclude ? id : not)(
1 === length(dctDays[k])
),
Object.keys(dctDays)
);
return filter(tpl => elem(snd(tpl), days), xs);
};

// monthsWithSingleDays :: Bool -> [(Month, Day)] -> [(Month, Day)]
const monthsWithSingleDays = blnInclude => xs => {
const
dctMonths = dictFromPairs(fst)(snd)(xs),
months = filter(
k => (blnInclude ? id : not)(
1 == length(dctMonths[k])
),
Object.keys(dctMonths)
);
return filter(tpl => elem(fst(tpl), months), 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();
})();</lang>
{{Out}}
<pre>[["July","16"]]</pre>


=={{header|Perl 6}}==
=={{header|Perl 6}}==