Fwd: Feature proposal

# Dmitry Shulgin (6 years ago)

---------- Forwarded message ---------- From: Dmitry Shulgin <shulhindvst at gmail.com>

Date: 2018-07-05 10:36 GMT+03:00 Subject: Feature proposal To: es-discuss at mozilla.org

I came across a task of finding an index of the last element in array that satisfies the condition, so i reversed array and found it by *findIndex * function. I was thinking: Why do we have findIndex` method, but no findLastIndex? It seems logical to follow [index|lastIndex]Of pair, doesn't it? Reversing array in this case seems too complicated to me.

So, i would like to suggest new method like Array.prototype.findLastIndex()

Example:

function isPrime(element, index, array) { var start = 2; while (start <= Math.sqrt(element)) { if (element % start++ < 1) { return false; } } return element > 1; }

console.log([4, 6, 8, 12].findIndex(isPrime)); // -1, not found console.log([7, 4, 6, 7, 12].findIndexLast(isPrime)); // 3

Would be glad to implement, if it makes sense.

Thx for replies.

P.S. Small issue on GitHub was closed due to offtop (not an issue, as i understand). tc39/ecma262#1253

# Cyril Auburtin (6 years ago)

there you go

console.log([7, 4, 6, 7, 12].findIndex((_, i, a) => isPrime(a[a.length-1-i]))); // 3

Le mer. 18 juil. 2018 à 11:17, Dmitry Shulgin <shulhindvst at gmail.com> a écrit :

# Cyril Auburtin (6 years ago)

sorry you get 1, and need again to subtract the array length, arr.length -1 - 1 to get the final result 3

Le mer. 18 juil. 2018 à 18:41, Cyril Auburtin <cyril.auburtin at gmail.com> a écrit :

# T.J. Crowder (6 years ago)

On Wed, Jul 18, 2018 at 5:44 PM, Cyril Auburtin <cyril.auburtin at gmail.com>

wrote:

sorry you get 1, and need again to subtract the array length, `arr.length -1

  • 1` to get the final result 3

So you can't just use the passed-in third argument, and need:

let a = [7, 4, 6, 7, 12];
console.log(a.length - 1 - a.findIndex((_, i) => isPrime(a[a.length-1-i])));

Strikes me as a reasonable argument for adding findLastIndex. ;-) (And perhaps findLast, since the same trick with find just won't work at all.)

Prior art (looking for instance at the libs that inspired the array additions in ES5 and ES2015):

  • Lodash implements both findLastIndex and findLast).
  • Underscore, PrototypeJS, and MooTools don't as far as I can see from the docs.

-- T.J. Crowder

# Michael Luder-Rosefield (6 years ago)

Is strikes me that every single Array method that takes an iteratee function (signature (value, index, array)) should be able to iterate through the array in reverse, in a standardised way.

At the moment, we have:

  • every
  • filter
  • find
  • findIndex
  • forEach
  • indexOf / lastIndexOf
  • map
  • reduce / reduceRight
  • some

which is not very consistent at all. Perhaps we could add an iterOrder method that changes the way the array is iterated through?

I propose it could take 1 parameter:

  • If -1 is passed in, the array is iterated through in reverse.
  • If 1,0 or undefined is passed through, the array is iterated through normally.
  • If an array of numbers is passed through, these represent the indexes in the order they will be processed.
  • Any illegible indexes in the passed-in array will be ignored
    • Any eligible indexes not given will be ignored
  • If a generator function is passed in, it should take the array (or a length value) and spit out indexes.
  • If a non-generator function is passed in, it should spit out an array of indexes, which will be used as-per arrays being passed in

Whether that iterOrder is reset after an iteration could go either way. I'd suggest it probably should, with an option to persist.

Example:

let arr = [ 8,4,7,3 ];

arr.iterOrder().map(x => x) === arr

arr.iterOrder(-1).map(x => x) === arr.reverse() === [ 3,7,4,8 ]

arr.iterOrder([ 2,1 ]).map(x => x) === [ 7,4 ]
// haven't got time to give function arg examples, but randomisers would be
fairly trivial

// original indexes are preserved during iteration:
arr.iterOrder(-1).forEach((val, i) => console.log(val, i))
// > 3, 3
// > 7, 2
// > 4, 1
// > 8, 0

This is a messy first-pass at the problem, and I can already see potential issues, but it would at least standardise and generalise the problem

# T.J. Crowder (6 years ago)

On Wed, Jul 18, 2018 at 6:31 PM, Michael Luder-Rosefield < rosyatrandom at gmail.com> wrote:

Is strikes me that every single Array method that takes an iteratee

function

(signature (value, index, array)) should be able to iterate through the array in reverse, in a standardised way.

Yeah, I had a similar thought. We're talking arrays, not generic iterables, after all.

My thought was some kind of reversed view of the array, something like this:

const reversedView = array => new Proxy(array, {
    get(target, prop, receiver) {
        if (isArrayIndex(prop)) {
            prop = target.length - 1 - prop;
        }
        return Reflect.get(target, prop, receiver);
    }
});

...but thought-through, tested, and efficient (which the above Is Not -- it does work in a basic couple of checks, but I'm running out the door and wouldn't be surprised if there are edge cases not handled: jsfiddle.net/80se3xqj). Then my mind sort of wanders off wanting to generalize this concept of a view on an array... :-)

Amusingly, this doesn't help at all with the original request here: findLastIndex (because it would return the index in the reversed view, not the original array). But it does with forEach, find, some, etc.:

const objects = [
    {id: 1, type: 1},
    {id: 2, type: 2},
    {id: 3, type: 2},
    {id: 4, type: 3}
];
console.log(reversedView(objects).find(o => o.type === 2));

Finds {id: 3, type: 2}.

-- T.J. Crowder

# Nicolas B. Pierron (6 years ago)

On Wed, Jul 18, 2018 at 5:31 PM, Michael Luder-Rosefield <rosyatrandom at gmail.com> wrote:

At the moment, we have:

every filter find findIndex forEach indexOf / lastIndexOf map reduce / reduceRight some

which is not very consistent at all. Perhaps we could add an iterOrder method that changes the way the array is iterated through?

Stupid question, but why are some of these methods implemented on String, Array, TypedArray, Map and Set, while all of them are Iteratable? If we were to add all these methods on Iteratable, then one could add a reverse generator property on Iteratable.

Iteratable.prototype.reverse = function* reverse() { … }; foo.reverse().forEach( e => console.log(e) )

Though, this implies that all these objects implement the Iteratable prototype, and I am not sure what impact this might have on the Web.

# Dmitry Shulgin (6 years ago)

Sorry, I got lost.

I partially agree with Michael Luder-Rosefield. The main reason I started this thread is consistency. Because I can't get the logic behind what Michael listed above. I don't think I can support the idea of iterOrder. Looks like a try to create silver bullet and I don't think it's a handy way to perform these examples. Looks more universal than handy to me.

I like the idea to expand this proposal for find method as well.

Thanks.

P. S. This is my first proposal, so I apologies for bad formatting and forwarding.

# Mike Samuel (6 years ago)

On Wed, Jul 18, 2018 at 2:06 PM Nicolas B. Pierron < nicolas.b.pierron at mozilla.com> wrote:

On Wed, Jul 18, 2018 at 5:31 PM, Michael Luder-Rosefield <rosyatrandom at gmail.com> wrote:

At the moment, we have:

every filter find findIndex forEach indexOf / lastIndexOf map reduce / reduceRight some

which is not very consistent at all. Perhaps we could add an iterOrder method that changes the way the array is iterated through?

Stupid question, but why are some of these methods implemented on String, Array, TypedArray, Map and Set, while all of them are Iteratable?

String is different from the others. The String index methods deal with a contiguous subsequence of the sequence, not the index of an element in the sequence.

If we were to add all these methods on Iteratable, then one could add a reverse generator property on Iteratable.

Reverse is problematic on String. The index of a codepoint can't be subtracted from String.length since length is not the count of codepoints.

# Isiah Meadows (6 years ago)

Just a note: there does exist a destructive method to do this: Array.prototype.reverse()

developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse

Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com

# Dmitry Shulgin (6 years ago)

Above we was talking about this. consistency and handy using -- two main reasons for this proposal.

reverse will also mutate my array, cause of in-place implementation.

You can find workaround for many existing methods, but it's not handy to my mind.

# Isiah Meadows (6 years ago)

What about this alternate proposal:

isiahmeadows/array-additions-proposal/blob/master/README.md#get-arrayprototypereversed-get-typedarrayprototypereversed

(I've got a myriad of other related stuff there, too.)

# T.J. Crowder (6 years ago)

On Thu, Jul 19, 2018 at 9:49 AM, Isiah Meadows <isiahmeadows at gmail.com> wrote:

What about this alternate proposal:

Great minds: esdiscuss.org/topic/fwd-feature-proposal#content-5 ;-)

-- T.J. Crowder

# Michael Luder-Rosefield (6 years ago)

This array view thing is very close to what I had in mind, and seems to suggest to a lot of interesting possibilities.

What strikes me as the two most significant are:

  • again, 'reversed' is just one alternative iteration order through, though almost certainly the most useful
  • others I can think of could be 'even/odd indexes', 'random shuffle', 'iterate-until /while-condition', and filter equivalents
  • as well as the view defining the iteration order, it could define the computed value
    • this would be basically a map where the value isn't computed until accessed; caching behaviour could be user
# Isiah Meadows (6 years ago)

I'd rather keep it restricted to very simple, context-free things like a reverse subtract or single add, with only a few bits of data necessary. Anything more is going to be a perf concern because you can't avoid visible side effects.

# Dmitry Shulgin (6 years ago)

Cool, gonna read it out. First impression -- it can make methods more complex and usage will be different between find, findlastIndex etc., if we want to use something like reverseView.

2018-07-19 11:56 GMT+03:00 T.J. Crowder <tj.crowder at farsightsoftware.com>:

# Dmitry Shulgin (6 years ago)

Yeah, i meant T.J. Crowder's comment that reverseView won't solve original issue.

2018-07-19 12:34 GMT+03:00 Isiah Meadows <isiahmeadows at gmail.com>:

# Nicolas B. Pierron (6 years ago)

On Wed, Jul 18, 2018 at 6:43 PM, Mike Samuel <mikesamuel at gmail.com> wrote:

On Wed, Jul 18, 2018 at 2:06 PM Nicolas B. Pierron <nicolas.b.pierron at mozilla.com> wrote:

Stupid question, but why are some of these methods implemented on String, Array, TypedArray, Map and Set, while all of them are Iteratable?

String is different from the others. The String index methods deal with a contiguous subsequence of the sequence, not the index of an element in the sequence.

I am not sure why this would prevent us from adding an Iteratable object with common properties. Iteratable.prototype.foo can exist at the same time as String.prototype.foo, and calling the foo property on a String will result in calling String.prototype.foo.

Also one could implement a naïve/default implementation of reverseOrder which allocates a temporary array for object which have no random accesses capabilities.

  Iteratable.prototype.reverseOrder = function* () {
      for (let e of [...this].reverse())
           yield e;
  }

If we were to add all these methods on Iteratable, then one could add a reverse generator property on Iteratable.

Reverse is problematic on String. The index of a codepoint can't be subtracted from String.length since length is not the count of codepoints.

Don't take me wrong, I was not suggesting to add a reverse property. I was suggesting to add the Iteratable object in the prototype chain such that someone can implement extra property on the Iteratable / Iteratable.prototype object.

Among list of nice Iteratable property that one might add, and which might not be part of the standard are things like: chain, take, skip, takeWhile, skipWhile, repeat, zip, enumerate, range, stepBy, …

Another option would be to just add a property named iter which returns an Iteratable object.

# Mike Samuel (6 years ago)

On Thu, Jul 19, 2018 at 8:54 AM Nicolas B. Pierron < nicolas.b.pierron at mozilla.com> wrote:

On Wed, Jul 18, 2018 at 6:43 PM, Mike Samuel <mikesamuel at gmail.com> wrote:

Reverse is problematic on String. The index of a codepoint can't be subtracted from String.length since length is not the count of codepoints.

Don't take me wrong, I was not suggesting to add a reverse property. I was suggesting to add the Iteratable object in the prototype chain such that someone can implement extra property on the Iteratable / Iteratable.prototype object.

My mistake. I assumed a goal was to allow implementing a generic findLastIndex in terms of find and reverse iterators.

# Nicolas B. Pierron (6 years ago)

On Thu, Jul 19, 2018 at 12:53 PM, Nicolas B. Pierron <nicolas.b.pierron at mozilla.com> wrote:

On Wed, Jul 18, 2018 at 2:06 PM Nicolas B. Pierron <nicolas.b.pierron at mozilla.com> wrote:

Stupid question, but why are some of these methods implemented on String, Array, TypedArray, Map and Set, while all of them are Iteratable?

Among list of nice Iteratable property that one might add, and which might not be part of the standard are things like: chain, take, skip, takeWhile, skipWhile, repeat, zip, enumerate, range, stepBy, …

Apparently this already exists as a JS library: fitzgen.github.io/wu.js

# Isiah Meadows (6 years ago)

I'm not a fan of the "Iteratable" object - it doesn't align with the corresponding existing concept of "iterables", and the iterable protocol is just an interface - there's no named object that corresponds to that type.

What we really need is that pipeline operator to finally get figured out: tc39/proposal-pipeline-operator

That wil address the primary need for things like these, without running you into issues of namespace collision.


Isiah Meadows me at isiahmeadows.com, www.isiahmeadows.com