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?
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; } 3You 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')5let 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}) 4Reversing 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);2You 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);1What 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; } } 1I 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); 3Please 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);4The 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")); 1This is what I used:
const lastIndex = (items.length -1) - (items.reverse().findIndex(el=> el.a === "something")) 3var 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); 1Simple 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 2You 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.
5You 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; } 2const 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