Find last index of element inside array by certain condition

Let's say I have an array of objects:

[ { 'a': 'something', 'b':12 }, { 'a': 'something', 'b':12 }, { 'a': 'somethingElse', 'b':12 }, { 'a': 'something', 'b':12 }, { 'a': 'somethingElse', 'b':12 } ] 

What would be the cleanest way to get the last index of an element where a has the value 'something' - in this case index 3? Is there any way to avoid loops?

3

23 Answers

Here's a reusable typescript version which mirrors the signature of the ES2015 findIndex function:

/** * Returns the index of the last element in the array where predicate is true, and -1 * otherwise. * @param array The source array to search in * @param predicate find calls predicate once for each element of the array, in descending * order, until it finds one where predicate returns true. If such an element is found, * findLastIndex immediately returns that element index. Otherwise, findLastIndex returns -1. */ export function findLastIndex<T>(array: Array<T>, predicate: (value: T, index: number, obj: T[]) => boolean): number { let l = array.length; while (l--) { if (predicate(array[l], l, array)) return l; } return -1; } 
3

You can use findIndex to get index. This will give you first index, so you will have to reverse the array.

var d = [{'a': "something", 'b':12}, {'a': "something", 'b':12}, {'a': "somethingElse", 'b':12}, {'a': "something", 'b':12}, {'a': "somethingElse", 'b':12}] function findLastIndex(array, searchKey, searchValue) { var index = array.slice().reverse().findIndex(x => x[searchKey] === searchValue); var count = array.length - 1 var finalIndex = index >= 0 ? count - index : index; console.log(finalIndex) return finalIndex; } findLastIndex(d, 'a', 'something') findLastIndex(d, 'a', 'nothing')
5
let newArray = yourArray.filter((each)=>{ return (each.a === something) }); newArray[newArray.length-1]; 

You can also do

let reversedArray = yourArray.reverse(); reversedArray.find((each)=>{return each.a === something}) 
4

Reversing the array did not sound very straightforward to me, so my solution to my very similar case was using map() and lastIndexOf():

var lastIndex = elements.map(e => e.a).lastIndexOf('something'); 

Upd.: this answer from the dupe post makes it even better with map(cond).lastIndexOf(true)

Upd.: though this is not the most efficient solution, as we have to always traverse all array, whereas if we ran from end, we could end search the moment we found first match (@nico-timmerman's answer).

You could iterate from the end and exit the loop if found.

var data = [{ a: 'something', b: 12 }, { a: 'something', b: 12 }, { a: 'somethingElse', b: 12 }, { a: 'something', b: 12 }, { a: 'somethingElse', b: 12 }], l = data.length; while (l--) { if (data[l].a ==='something') { break; } } console.log(l);
2

You could use Lodash:

import findLastIndex from "lodash/findLastIndex" const data = [ { a: 'something', b: 12 }, { a: 'something', b: 12 }, { a: 'somethingElse', b: 12 }, { a: 'something', b: 12 }, { a: 'somethingElse', b: 12 }, ] const lastIndex = findLastIndex(data, v => v.a === "something") 

Update - Array.prototype.findLastIndex is now available for use -

var d = [{'a': "something", 'b':12}, {'a': "something", 'b':12}, {'a': "somethingElse", 'b':12}, {'a': "something", 'b':12}, {'a': "somethingElse", 'b':12}] const lastIdx = d.findLastIndex(n => n.a === 'something'); console.log(lastIdx);

**Please check out this link to see what browsers are supporting findLastIndex before using it

You may also achieve this using reverse and findIndex but the performance is less better then using findLastIndex

var d = [{'a': "something", 'b':12}, {'a': "something", 'b':12}, {'a': "somethingElse", 'b':12}, {'a': "something", 'b':12}, {'a': "somethingElse", 'b':12}] const lastIdx = d.reverse().findIndex(n => n.a === 'something'); console.log(lastIdx);

findLastIndex -

findIndex -

reverse -

1

What about something like this in ES6:

 arr.indexOf(arr.filter(item => item.a === 'something').pop()) 

First, this is not an array of objects but an array of arrays. An array of objects would look like this:

[{'a': something, 'b':12}, {'a': something, 'b':12}, {'a': somethingElse, 'b':12}, {'a': something, 'b':12}, {'a': somethingElse, 'b':12}] 

Generally it's a good practice to use object syntax when you use non-numeric indices.

Second, to answer your question you can just use a reverse loop:

for(let i=(arr.length - 1); i>=0; i--){ if(arr[i].a === "something"){ index = i; break; } } 
1

I wonder why lots of people would like to always avoid loops nowadays. They are just as natural structures as ifs and pure sequence of code lines. To avoid repeating yourself, write a function for it, what you even could add to Array.prototype. The following is a simple example, not tested, just for the idea.

For getting the index

Array.prototype.lastIndex = function(cond) { if (!this.length) return -1; if (!cond) return this.length-1; for (var i=this.length-1; i>=0; --i) { if (cond(this[i])) return i; } return -1; } 

Or for elements directly

Array.prototype.lastOrDefault = function(cond, defaultValue) { if (!this.length) return defaultValue; if (!cond) return this[this.length-1]; for (var i=this.length-1; i>=0; --i) { if (cond(this[i])) return this[i]; } return defaultValue; } 

Usage example:

myArr = [1,2,3,4,5]; var ind1 = myArr.lastIndex(function(e) { return e < 3; }); var num2 = myArr.lastOrDefault(function(e) { return e < 3; }); var num8 = myArr.lastOrDefault(function(e) { return e > 6; }, /* explicit default */ 8); 
3

Please take a look at this method.

var array=[{a: 'something', b:12}, {a: 'something', b:12}, {a: 'somethingElse', b:12}, {a: 'something', b:12}, {a: 'somethingElse', b:12} ]; console.log(array.filter(function(item){ return item.a=='something'; }).length);
4

The cleanest way i found was using a single reduce. No need to reverse the array or using multiple loops

const items = [ {'a': 'something', 'b': 12}, {'a': 'something', 'b': 12}, {'a': 'somethingElse', 'b': 12}, {'a': 'something', 'b': 12}, {'a': 'somethingElse', 'b': 12} ]; function findLastIndex(items, callback) { return items.reduce((acc, curr, index) => callback(curr) ? index : acc, 0); } console.log(findLastIndex(items, (curr) => curr.a === "something")); 
1

This is what I used:

const lastIndex = (items.length -1) - (items.reverse().findIndex(el=> el.a === "something")) 
3
var arr = [ {'a': 'something', 'b':12}, {'a': 'something', 'b':12}, {'a': 'somethingElse', 'b':12}, {'a': 'something', 'b':12}, {'a': 'somethingElse', 'b':12} ]; var item_count = 0; var traverse_count = 0; var last_item_traverse_count = 0; arr = arr.reverse(); arr.filter(function(element) { traverse_count += 1; if(item_count < 1 && element.a == 'something') { last_item_traverse_count = traverse_count; item_count += 1; return true; } return false; }); var item_last_index = arr.length - last_item_traverse_count; console.log(item_last_index); 

I hope this code definitely works. The code may not follow naming conventions. I'm sorry about that. You can just name those variables however you want.

here what i used to get the last active element :

return this._scenarios.reverse().find(x => x.isActive); 
1

Simple and fast:

const arr = [ {'a': 'something', 'b': 12}, {'a': 'something', 'b': 12}, {'a': 'somethingElse', 'b': 12}, {'a': 'something', 'b': 12}, {'a': 'somethingElse', 'b': 12} ]; let i = arr.length; while(i--) if (arr[i] === 'something') break; console.log(i); //last found index or -1 
2

You can just map the the list to a and then use lastIndexOf:

const array = [ {'a': 'something', 'b':12}, {'a': 'something', 'b':12}, {'a': 'somethingElse', 'b':12}, {'a': 'something', 'b':12}, {'a': 'somethingElse', 'b':12} ]; const result = array.map(({a}) => a).lastIndexOf('something'); console.log(result);

Update - 27 October 2021 (Chrome 97+)

You can now use findLastIndex:

const array = [ {'a': 'something', 'b':12}, {'a': 'something', 'b':12}, {'a': 'somethingElse', 'b':12}, {'a': 'something', 'b':12}, {'a': 'somethingElse', 'b':12} ]; const last_index = array.findLastIndex((item) => item.a === 'something'); // → 3 

Read more here.

5

You can use reverse and map together to prevent from mutating the original array.

var arr = [{'a': 'something', 'b':12}, {'a': 'something', 'b':12}, {'a': 'somethingElse', 'b':12}, {'a': 'something', 'b':12}, {'a': 'somethingElse', 'b':12}]; var index = arr.length - 1 - arr.map(x => x) .reverse() .findIndex(x => (x.a === 'something')) if (index === arr.length) { index = -1; } 
2
const arrSome = [{'a': 'something', 'b':12}, {'a': 'something', 'b':12}, {'a': 'somethingElse', 'b':12}, {'a': 'something', 'b':12}, {'a': 'somethingElse', 'b':12}] const lastIndex = arrSome.reverse().findIndex(el=> el.a == 'something');//? let index; if (~lastIndex) { index =arrSome.length - lastIndex; } else { index = -1 } console.log(index) 

You can use Math.max apply to the array that match your condition with Array.prototype.map()

like this example

private findLastIndex() { const filter = this.myArray.map((t, index) => { if ( // your condition ) { return index; } else { return -1; } }); return Math.max.apply(null, filter); } 

Here's a solution using no (manual) loops, and without mutating the calling array:

const arr = [ {'a': 'something', 'b': 12}, {'a': 'something', 'b': 12}, {'a': 'somethingElse', 'b': 12}, {'a': 'something', 'b': 12}, {'a': 'somethingElse', 'b': 12} ]; function findLastIndex(arr, callback, thisArg) { return (arr.length - 1) - // Need to subtract found, backwards index from length arr.reduce((acc, cur) => [cur, ...acc], []) // Get reversed array .findIndex(callback, thisArg); // Find element satisfying callback in rev. array } console.log(findLastIndex(arr, (e) => e.a === "something"));

Reference:

It should be noted that the reducer used here is vastly outperformed by arr.slice().reverse(), as used in @Rajesh's answer.

why not just get the length and use as a array pointer e.g.

var arr = [ 'test1', 'test2', 'test3' ]; 

then get the last index of the array by getting the length of the array and minus '1' since array index always starts at '0'

var last arrIndex = arr[arr.length - 1]; 
1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like