Ramda pick for fp-ts options / maybe - ramda.js

Using fp-ts. I have an option of an array
const arrayofKeys: Option<Array<K>>,
and an option of a record
const record: Option<Record<K,V>>
I want to pick the Vs of the Record where Ks intersect with the Array and stick the result in an Option.
In ramda: R.pick(arrayOfKeys, record)
How do i solve this with fp-ts or other packages within the fp-ts ecosystem?

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]).

Ramda's lift lifts a function on some values to work on a container of those values. So lift (pick) will likely do what you want, so long as fp-ts's Option supports the FantasyLand Apply specification.
const {of} = folktale.maybe
const {lift, pick} = R
const keys = of (['k', 'e', 'y', 's']) // Maybe (['k', 'e', 'y', 's'])
const record = of ({s: 1, k: 2, y: 3, b: 4, l: 5, u: 6, e: 7}) // Maybe ({s: 1, k: 2, ...})
console .log (lift (pick) (keys, record) .toString())
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/folktale/2.0.0/folktale.min.js"></script>

This is a great use case for traverseArray, an optimized version of traverse. You can also use "Do notation" and apS to get a really clean, monadic pipeline. If any of these operations return a None, the entire flow will terminate early (this is a good!).
Also, lookup is a very handy function similar to get from Ramda/Lodash, but it returns an Option. Both the Record and Array modules export a version of this function.
declare const arrayofKeys: O.Option<Array<string>>
declare const record: O.Option<Record<string, number>>
export const result: O.Option<ReadonlyArray<number>> = pipe(
O.Do,
O.apS('keys', arrayofKeys),
O.apS('rec', record),
O.chain(({ keys, rec }) =>
pipe(
keys,
O.traverseArray(key => pipe(rec, R.lookup(key)))
)
)
)
Functions used:
https://gcanti.github.io/fp-ts/modules/Option.ts.html#do
https://gcanti.github.io/fp-ts/modules/Option.ts.html#aps
https://gcanti.github.io/fp-ts/modules/Option.ts.html#chain
https://gcanti.github.io/fp-ts/modules/Option.ts.html#traversearray
https://gcanti.github.io/fp-ts/modules/Record.ts.html#lookup

Related

How can I optimize finding the shortest description of a set

The goal is to find the optimal description of a subset of countries, using a combination of individual countries and groups of countries that are predefined.
Set of countries is comprised of all 2 letter ISO country codes, (US/DE/NL/etc)
Groups of countries are fixed, for example:
GROUP1: NL,BE,LU
GROUP2: NL,BE,LU,CZ,US,FI,NO,GI,FR,DK
I want to have a representation of this list of countries:
CZ,US,FI,NO,GI,FR,DK,DE
I would like to shorten this using the defined groups, to the representation with the least amount of elements. For this example that would be:
+GROUP2 +DE -GROUP1
Because if we expand this back out we get:
Add GROUP2: NL,BE,LU,CZ,US,FI,NO,GI,FR,DK
Add DE
Remove GROUP1: NL,BE,LU
Which brings us back to CZ,US,FI,NO,GI,FR,DK,DE
Any pointers to libraries or examples are welcome in any programming language
I'm at a loss what a good approach would be. I've tried looking into existing algorithms, comparable problems, but I can't seem to wrap my head around the problem and find a good fit.
Just to illustrate a simple brute force solution that will obviously not scale:
let countries = ['C1', 'C2', 'C3', 'C4', 'C5', 'C6']
let groups = {
G1: ['C1', 'C2', 'C3', 'C4', 'C5'],
G2: ['C1', 'C4'],
}
let output = getBest(['C2', 'C3', 'C5', 'C6'])
// output == ["+C6", "+G1", "-G2"]
function getBest(input) {
const ids = countries.concat(Object.keys(groups))
let best = input
for (const t of combinations(ids)) {
if (expand(t, groups).sort().toString() == input.toString()) {
if (t.length < best.length) best = [...t]
}
}
return best
}
// Expands short form to long form
function expand(s, groups) {
return Array.from(
s.sort().reduce((acc, curr) => {
let symbol = curr[0]
let id = curr.slice(1)
if (groups[id]) {
curr = groups[id]
} else {
curr = [id]
}
if (symbol == '+') {
return new Set([...acc, ...curr])
} else {
return new Set([...acc].filter((a) => !curr.includes(a)))
}
}, new Set())
)
}
// Yields all possible short form options
function* combinations(array) {
array = ['+', '-'].reduce((acc, curr) => {
return acc.concat(array.map((s) => curr + s))
}, [])
for (const subset of subsets(array)) {
yield subset
}
}
// Creates powerset of array
function* subsets(array, offset = 0) {
while (offset < array.length) {
let first = array[offset++]
for (let subset of subsets(array, offset)) {
subset.push(first)
yield subset
}
}
yield []
}
To me, the problem does not sound like there exists a classical model for it with a well-known polynomial-time algorithm.
A reasonable approach looks as follows.
Consider formulas in a formal language, like (G1 subtract G2) unite (G3 intersect G4), where Gi are predefined groups (and perhaps individual countries, but that will slow the solution down a lot).
Each formula's score is its length plus the size of difference with the desired answer (so that we add or subtract individual elements as the last step of the formula).
Now, for formulas of lengths 0, 1, 2, ..., (iterative deepening), recursively generate all possible formulas of such length and consider their score.
Stop when the length reaches the score of the best answer so far.
There's some room to optimize (for example, prune clearly stupid branches like Gi symdiff Gi, or perhaps memoize the shortest formula for each set we can obtain), but the solution is nevertheless exponential in the number of items and groups.

Passing multiple arguments and result of last function through pipe

I'm building a pipe with Ramda.js which accepts three arguments. The first function needs those three arguments, and it's result is used in the second function. However, the second function also needs one of the initial arguments. I cannot figure out the branching to build something like it.
In pseudocode style, I need something like this:
const composedFunction = R.pipe(
firstFunction,
secondFunction,
);
const firstFunction = (reusedArgument, secondArgument, thirdArgument) => someAnswer;
const secondFunction = (reusedArgument, someAnswer);
console.log(composedFunction({ foo: bar }, [5, 3, 4], [100, 12, 12]));
I can think of a few solutions:
Wrap your pipe inside another function so that functions in your composition can still refer to the original parameters.
Here func2 accepts the output of func1 but also has access to the initial b parameter. Obviously func2 must be curried and be designed to accept its "data" as the last parameter (which is a tenet of Ramda and functional programming in general I'd say).
const func3 = (a, b, c) =>
pipe(func1, func2(b))
(a, b, c);
func3(10, 20, 30);
Other option, func1 returns an array which you can destructure in func2.
I don't think this is particularly nice but it is an option:
const func1 = (a, b, c) => [a + c, b];
const func2 = ([sum, b]) => sum * b;
const func3 = pipe(func1, func2);
func3(10, 20, 30);
I think the simplest thing here is to not bother with Ramda's pipe function, which is not designed to handle such case, and just write it manually:
const func1 = (a, b, c) => `func1 (${a}, ${b}, ${c})`
const func2 = (a, d) => `func2 (${a}, ${d})`
const func3 = (a, b, c) => func2 (func1 (a, b, c), a)
console .log (func3 ('a', 'b', 'c'))
Ramda has recently been considering a way to make this easier for longer pipelines; even with that, though, the above is probably simpler for just a few functions.

What's the lodash/fp equivalent of Ramda's 'when' function?

Looking at lodash and it's fp facilities., I am searching for when or ifElse equivalent.
In Ramda, one can use when to do semi shorthand if. Check predicate on sent data, and when true, do something. When false, return input data unchanged.
// truncate :: String -> String
var truncate = R.when(
R.propSatisfies(R.gt(R.__, 10), 'length'),
R.pipe(R.take(10), R.append('…'), R.join(''))
);
truncate('12345'); //=> '12345'
truncate('0123456789ABC'); //=> '0123456789…'
How this will be accomplished in lodash?
I don't know how to do this in lodash/fp. (One of these days, I swear I spend some time learning more about it!) But do note that the version as written could well be simplified.
First, keeping it in Ramda (disclaimer: I'm one of the authors), but simplifying your functions with simple ES6-style lambdas:
// truncate :: String -> String
var truncate = R.when(
s => s.length > 10,
s => s.slice(0, 10) + '…'
);
truncate('12345'); //=> '12345'
truncate('0123456789ABC'); //=> '0123456789…'
I find this version extremely readable, and might leave it at that. But you can also remove the library altogether by replacing the when with another ES6-lambda and using a conditional expression:
// truncate :: String -> String
var truncate = s => s.length > 10 ? s.slice(0, 10) + '…' : s;
Point-free is a great technique that can often add readability. But there are few reasons to use it when it obscures meaning.
In lodash fp you should 'cond' for this:
const showTen = fp.pipe(
fp.slice(0, 10),
fp.join(''),
fp.add(fp.__, '...')
);
const gtThanTen = fp.pipe(
fp.result('length'),
fp.lt(10)
);
const showOnlyTen = fp.cond([
[gtThanTen, showTen],
[fp.stubTrue, fp.identity]
]);
showOnlyTen('12345678901');

How do I type lodash/fp curried functions?

I'm trying to add to the flow-typed typings. I'm starting with the lodash/fp module because that is most useful to myself. However, I'm struggling with how to type it correctly.
A simple example if dropRightWhile:
declare function dropRightWhile<T>(iteratee: (val: T)=>boolean, data?: Array<T>): Array<T> | (data: Array<T>)=>Array<T>;
This is my attempt to type it. dropRightWhile must take an iteratee, and it could take the data. If you don't give it data then it returns a function that takes the data, but if you give it data then it returns an array.
The type I have made doesn't strictly make the connection between number of arguments and return type. However, when I try to use a curried dropRightWhile I get an error
var c = dropRightWhile((x) => x> 0); c([0, 1, 2, 3]);
error: Function cannot be called on array type
I would've thought that since dropRightWhile can return a function then I would be able to call it, but it seems the array type is getting in the way.
Any suggestions?
You can define 2 overloadings
declare function dropRightWhile<T>(iteratee: (val : T) => boolean, data : Array<T>) : Array<T>;
declare function dropRightWhile<T>(iteratee: (val : T) => boolean): (data : Array<T>) => Array<T>;
// tests
const iteratee = x => x > 0;
(dropRightWhile(iteratee)([0, 1, 2, 3]) : Array<number>);
(dropRightWhile(iteratee, [0, 1, 2, 3]) : Array<number>)

Should there be an indicesWhere method on Scala's List class?

Scala's List classes have indexWhere methods, which return a single index for a List element which matches the supplied predicate (or -1 if none exists).
I recently found myself wanting to gather all indices in a List which matched a given predicate, and found myself writing an expression like:
list.zipWithIndex.filter({case (elem, _) => p(elem)}).map({case (_, index) => index})
where p here is some predicate function for selecting matching elements. This seems a bit of an unwieldy expression for such a simple requirement (but I may be missing a trick or two).
I was half expecting to find an indicesWhere function on List which would allow me to write instead:
list.indicesWhere(p)
Should something like this be part of the Scala's List API, or is there a much simpler expression than what I've shown above for doing the same thing?
Well, here's a shorter expression that removes some of the syntactic noise you have in yours (modified to use Travis's suggestion):
list.zipWithIndex.collect { case (x, i) if p(x) => i }
Or alternatively:
for ((x,i) <- list.zipWithIndex if p(x)) yield i
But if you use this frequently, you should just add it as an implicit method:
class EnrichedWithIndicesWhere[T, CC[X] <: Seq[X]](xs: CC[T]) {
def indicesWhere(p: T => Boolean)(implicit bf: CanBuildFrom[CC[T], Int, CC[Int]]): CC[Int] = {
val b = bf()
for ((x, i) <- xs.zipWithIndex if p(x)) b += i
b.result
}
}
implicit def enrichWithIndicesWhere[T, CC[X] <: Seq[X]](xs: CC[T]) = new EnrichedWithIndicesWhere(xs)
val list = List(1, 2, 3, 4, 5)
def p(i: Int) = i % 2 == 1
list.indicesWhere(p) // List(0, 2, 4)
You could use unzip to replace the map:
list.zipWithIndex.filter({case (elem, _) => p(elem)}).unzip._2