Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatic clojure and Javascript expression for filtering a sequence based on the previous value

So I have a seq of integers. I'd like to pick every other element but only if the element preceding it is positive. For instance, given this input:

4, 9, -1, 8, 3, 20, -1, 7

I want to get this output:

9, 20

(Context: these values represent segments of text where a certain style has been applied. The 4, 9 pair indicates the user selected characters 4, 9 (exclusive) and clicked on "Bold". When the user "Unblods" only the first member of each pair becomes -1. So in order to find all "active" styles I need to filter those with -1 out)

I am looking for both a Javascript and a Clojure solution (as this code needs to run both in the browser side and the server side).

Here's a possible JS impl.:

styleList.map(function(curr, i) {
  if (i % 2 == 1) 
    return (styleList[i - 1] >= 0) ? curr : -1;
  else
    return -1;
}).filter(function(curr) { return curr >= 0 })

I don't like this solution: the function that I pass to .map() uses the index i to inspect the previous value. This feels awkward. I am looking for a solution which is more functional.

Edit

Here's another solution which I don't like (again: not very functional due to the mutations to the arr object):

var arr = [];
styleList.reduce(function(prev, curr) {
  arr.push(prev == -1 ? -1 : curr);
  return curr;
});
arr.filter(function(curr, i) {
  return i % 2 == 1 && curr >= 0;
});

Edit 2

Here's what @CandiedOrange has suggested (JS impl. using the underscore library):

_.zip(styleList, styleList.slice(1)).
  slice(0, -1).
  filter(function(pair, i) { return i % 2 == 0 }).
  filter(function(pair) { return pair[0] >= 0 }).
  map(function(pair) { return pair[1] })

comments:

  • this can be shortened a bit by combining the two .filter calls. Still, I think it is better to keep them separated as the i % 2 == 0 is an artifact of the impl. whereas the pair[0] >= 0 is the "real business logic" needed here.
  • this looks a bit verbose. I would have liked functional programming to handle such a task better. Is there any better solution in Clojure-land?

Edit 3

What's bothering me here, is the non functional (imperative) solution is quite trivial:

var result = [];
styleList.forEach(function(curr, i) {
  if (i % 2 == 1 && styleList[i - 1] >= 0)
    result.push(curr);
});

Sure it's not as terse as the Clojure solution but relies on fewer constructs so a JS developer is more likely to arrive at this than a Clojure developer is likely to arrive at the Clojure solution (which requires acquaintance with ->> partition first second map and filter)

like image 453
Itay Maman Avatar asked Aug 28 '26 20:08

Itay Maman


1 Answers

In Clojure:

(->> your-list 
     (partition 2)
     (filter #(pos? (first %)))
     (map second))

Edit: explanation as requested by @CandiedOrange:

  • Partition your-list into pairs
  • Filter the pairs keeping only the ones where the first element is positive
  • Transform each pair into a single item (the second)
like image 66
Diego Basch Avatar answered Aug 30 '26 08:08

Diego Basch



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!