neezer
neezer

Reputation: 20560

"Alter" head of list, then continue processing the list?

Not grasping the functional solution to this. I'm specifically working with ramda.js, but I believe the problem applies to any functional language.

Looking to return a new list with a head that is a modified copy of the existing head, then map over the entire list after the substitution has been made. I want to do special/extra logic for the head of the list when mapping over the list.

Here's a pseudo-code:

R.compose(
  R.map(mapsValuesToDOMNodes), // <-- ex. returns radio buttons, with the first one
                               //         selected, from previous "isFirst" prop
  // <-- ??? "modify" the head of the list; ex., add "isFirst" prop
  R.sortBy(aPropInEachValue),
  R.values
)(myObj);

NOTE: I'm hopefully looking for a language-agnostic solution, and I want to understand the functional approach to this problem.

Upvotes: 1

Views: 83

Answers (3)

Casey Benko
Casey Benko

Reputation: 2308

This may be the example you are looking for:

var x = [{a: 1}, {a: 2}, {a: 3}, {a: 4}];

var headLens = R.lens(
  R.head, 
  R.converge(R.prepend, [R.identity, R.flip(R.tail)])
);

var f = R.pipe(
  R.over(headLens, R.assoc("isFirst", true)), //use the lens to work only on the head
  R.map(R.evolve({a: R.add(2)})) //map through the entire list
);

var y = f(x);

console.log(y); // [{"a": 3, "isFirst": true}, {"a": 4}, {"a": 5}, {"a": 6}]
<script src="//cdn.jsdelivr.net/ramda/latest/ramda.min.js"></script>

What is going on here is that a lens is created with a setter that prepends the new head value to the tail of the old list.

If javascript had pattern matching then it would be much easier. Here's an example, in F#, that applies a function to only the head and returns a list with the function result as new head.

let mapHead f (h::t) = f h :: t //described function

let l = [1;2;3;4] //declare list of 4 ints

let r = mapHead ((+) 2) l //add 2 to head and return entire list

r //[3;2;3;4]

Upvotes: 0

ECWyne
ECWyne

Reputation: 433

You can could use either R.lensPath or R.assocPath

R.assocPath([0, 'isFirst'], true);

Upvotes: 0

ryachza
ryachza

Reputation: 4540

I'm not sure about with rambda.js, but functionally I think the concept would be to extract the head and tail (uncons), then apply a function to the head and cons it to the mapped tail.

In Haskell, something like:

applyFirstAndMap :: (a -> b) -> (a -> b) -> [a] -> [b]
applyFirstAndMap _  _   []     = []
applyFirstAndMap fx fxs (x:xs) = fx x:map fxs xs

And then:

applyFirstAndMap (+ 1) (+ 2) [1,2,3] == [2,4,5]

Upvotes: 2

Related Questions