I'd personally avoid Ramda et al as in my experience they're not very well typed. Here's a pure fp-ts approach (Str.fromNumber
is from fp-ts-std, trivially replaced):
declare const arrayOfKeyNums: Option<Array<number>>
const arrayOfKeys = pipe(arrayOfKeyNums, O.map(A.map(Str.fromNumber)))
declare const record: Option<Record<string, number>>
const keyIntersectedVals: O.Option<Array<number>> = pipe(
sequenceT(O.Apply)(arrayOfKeys, record),
O.map(([ks, rec]) =>
pipe(
rec,
R.foldMapWithIndex(Str.Ord)(A.getMonoid<number>())((k, v) =>
A.elem(Str.Eq)(k)(ks) ? [v] : [],
),
),
),
)
It's a bit verbose owing to the need to pass typeclass instances around. On the plus side, the use of typeclass instances means that this can be trivially updated to support any value type, including non-primitive types with any given Eq
.
Here's what the body might instead look like in Haskell for comparison, where typeclass instances don't need to be passed around:
keyIntersectedVals :: Maybe [Int]
keyIntersectedVals = uncurry (M.foldMapWithKey . intersectedToList) <$> sequenceT (mkeys, mmap)
where intersectedToList ks k v
| k `elem` ks = [v]
| otherwise = []
For example, given keys O.some(["a", "c"])
and a record O.some({ a: 123, b: 456, c: 789 })
, we get O.some([123, 789])
.