Add `takeWhile` native function

What?

Add takeWhile function for Array type to get the elements from the beginning while the element satisfy a condition of the function passed

const array  = ["foo", "foo","foo","foo" ,"bar", "foo", "foo", "bar"]

array.takeWhile((item) => item == "foo" )
// ["foo", "foo","foo","foo" ]

1 Like

I could see something like this being a valuable addition to the iterator helpers proposal, which would allow us to use takeWhile on any iterator.

I tried looking through the issues on that repo, to see if it's been discussed before, but I couldn't find much. The idea was mentioned in passing in this thread, but was not further discussed, so it may be worth opening up an issue there about it as well.

Of course, implementing this on iterators would be a little different than implementing this on arrays, but I think they would be more valuable for iterators. It's not too difficult to replicate takeWhile with normal arrays (it's a little verbose, but not horrible).

const array  = ["foo", "foo","foo","foo" ,"bar", "foo", "foo", "bar"]

const index = array.findIndex(item => item !== 'foo')
const result = index === -1 ? array : array.slice(0, index)
2 Likes

Off course, I may have been inaccurate when I said Array. Array is a iterable type, alright?

Its not horrible, but not very readable, demands more time to understand instead of takeWhile native function.

So, about this repo of propouse that you mentioned, I can open a issue about this function, or I have to discuss more here? I ask because its no very clear, for me, the roadmap until a real propose.

An array is an iterable, but it is not an iterator, so to use any of those iterator helper functions, you actually have to first convert it to an iterator as follows:

yourArray
  .values()
  .takeWhile(item => item!== 'foo')

i.e. using Array.prototype.values()

What you'll end up with another iterator. If you want to convert it back to an array, you would have to do this:

yourArray
  .values()
  .takeWhile(item => item!== 'foo')
  .toArray()

So, there's, unfortunately, a bit of plumbing if we choose to put this on the iterator helpers instead of on the array. But, on the plus side, it makes this helper function available to all iterators, not just arrays.

Feel free to open up an issue on that repo if you think it would be appropriate to include it as an iterator helper - anyone can open up an issue at any time on the proposal repos to discuss anything related to that proposal. You can then link this thread to that repo and/or vice-verca. However, It's possible that my suggestion isn't quite what you're wanting, and you'd rather just have a native Array method, in which case, conversation can certainly continue here. Or, maybe we ought to add such a method to both places.

1 Like

@IncognitaDev : Very good idea!
@theScottyJam :

... It's not too difficult to replicate takeWhile with normal arrays (it's a little verbose, but not horrible).

All existing methods could be replicated (in a verbose and maybe not horrible way); then what is the purpose of their existence? Maybe to make our lives easier, to be natively standardized, to be natively optimized, ...?

1 Like

That's the idea, functional languages have so much native functions which you just have to make specifics functions, generic functions are standard.

I'm certainly not against adding this method to arrays. However, I was just pointing out that it's currently not too difficult to hand-make a takeWhile function for arrays, compared to the difficulty level of doing so on iterators, thus, we may get more traction if we focus this proposal on iterators instead. And, judging by how the iterator-helpers proposal works, if this method gets added to all iterators, they'll also add it to arrays, because they want a one-to-one relationship between those methods when it's reasonable to do so.

From what I can tell, you would have to write code like this to implement takeWhile for iterators. I don't believe any of the proposed iterator-helpers will help simplify this task.

function* takeWhile(iterator, predicate) {
  for (const value of iterator) {
    if (!predicate(value) {
      break
    }
    yield value
  }
}

Which, isn't overly complicated to do either, but it's certainly a little more work than the array version. (Honestly, I thought it might have been a little more complicated than this).

Some methods, like flatMap(), exist purely for optimization purposes. For methods such as takeWhile(), where the primary focus is about "making our lives easier", then it's important to discuss "how much easier we're making our lives". For example:

  • array.at() is trivial to implement in userland, but it's been natively added anyways since its usage is extremely common.
  • Other methods are less commonly used, but get added, because it's tricky/impossible to add them correctly in userland, and they get used enough to warrant their addition.
  • Some proposals simply don't make it far, because they simply don't provide enough value in comparison to how easy it is to implement it in userland.

Because of this, any discussion around new methods will usually involve someone explaining how easy/difficult it is to implement such a feature in userland, because a discussion would be incomplete without acknowledging the existing difficulty level of such a feature. (Also, every once in a while, the O.P. would realize that a native implementation is simpler than they thought, and will drop their idea - you never know. I've certainly done that).

I have observed reading various ideas that the answers are more in a "fluid-flow" manner. It is not clear if the person responding is in favor or against an idea. Most of the time, a sort of negativism is included; and please do not confuse it with judgement. Now I understand that there is also a matter of subjectivity ie "how much easier we're making our lives".

What is the benefit of a small spoon when a standard spoon can be used for the same task 9 on 10 times without problem? :thinking: How much easier does the small spoon make my life? :thinking: Or it makes it a little more elegant like what a prettifier does to the code? :wink:

That's just a general thing about discussions here - the question isn't merely "why should we add it", but also "why are the reasons not to add it not compelling". (And TC39 has a lot of contrarians who feel very strongly that this should always be accounted for.) So you really have to address both sides to get anywhere with proposals here.