Personally, I wouldn't choose either solution. Here is why:
LastIndexOf:
The problem lies in the comparing of elements while searching through the array. It does compare the elements using strict equality. Therefore comparing objects will always fail, except they are the same. In OP case they are different.
Slice & reverse one-liner @adeneo
Given an array of three elements [{key: A},{key: B},{key: C}] and the lookup for the last index of key = D will give you an index of 3. This is wrong as the last index should be -1 (Not found)
Looping through the array
While this is not necessarily wrong, looping through the whole array to find the element isn't the most concise way to do it. It's efficient yes, but readability can suffer from it. If I had to choose one, I'd probably choose this one. If readability / simplicity is your friend, then below is yet one more solution.
A simple solution
We can make lastIndexOf work, we just need to make the value comparable (strict equality conform). Or simply put: we need to map the objects to a single property that we want to find the last index of using javascript's native implementation.
const arr = [ { key: "a" }, { key: "b" }, { key: "c" }, { key: "e" }, { key: "e" }, { key: "f" } ];
arr.map(el => el.key).lastIndexOf("e"); //4
arr.map(el => el.key).lastIndexOf("d"); //-1
// Better:
const arrKeys = arr.map(el => el.key);
arrKeys.lastIndexOf("c"); //2
arrKeys.lastIndexOf("b"); //1
A fast solution
Simple backwards lookup (as concise and as fast as possible). Note the -1 return instead of null/undefined.
const arr = [ { key: "a" }, { key: "b" }, { key: "c" }, { key: "e" }, { key: "e" }, { key: "f" } ];
const lastIndexOf = (array, key) => {
for(let i = array.length - 1; i >= 0; i--){
if(array[i].key === key)
return i;
}
return -1;
};
lastIndexOf(arr, "e"); //4
lastIndexOf(arr, "x"); //-1
Answer from Tom Siwik on Stack OverflowPersonally, I wouldn't choose either solution. Here is why:
LastIndexOf:
The problem lies in the comparing of elements while searching through the array. It does compare the elements using strict equality. Therefore comparing objects will always fail, except they are the same. In OP case they are different.
Slice & reverse one-liner @adeneo
Given an array of three elements [{key: A},{key: B},{key: C}] and the lookup for the last index of key = D will give you an index of 3. This is wrong as the last index should be -1 (Not found)
Looping through the array
While this is not necessarily wrong, looping through the whole array to find the element isn't the most concise way to do it. It's efficient yes, but readability can suffer from it. If I had to choose one, I'd probably choose this one. If readability / simplicity is your friend, then below is yet one more solution.
A simple solution
We can make lastIndexOf work, we just need to make the value comparable (strict equality conform). Or simply put: we need to map the objects to a single property that we want to find the last index of using javascript's native implementation.
const arr = [ { key: "a" }, { key: "b" }, { key: "c" }, { key: "e" }, { key: "e" }, { key: "f" } ];
arr.map(el => el.key).lastIndexOf("e"); //4
arr.map(el => el.key).lastIndexOf("d"); //-1
// Better:
const arrKeys = arr.map(el => el.key);
arrKeys.lastIndexOf("c"); //2
arrKeys.lastIndexOf("b"); //1
A fast solution
Simple backwards lookup (as concise and as fast as possible). Note the -1 return instead of null/undefined.
const arr = [ { key: "a" }, { key: "b" }, { key: "c" }, { key: "e" }, { key: "e" }, { key: "f" } ];
const lastIndexOf = (array, key) => {
for(let i = array.length - 1; i >= 0; i--){
if(array[i].key === key)
return i;
}
return -1;
};
lastIndexOf(arr, "e"); //4
lastIndexOf(arr, "x"); //-1
With ES2015 and findIndex you can pass a callback to look for an objects key.
If you make a copy of the array, and reverse it, you can find the last one by subtracting that index from the total length (and 1, as arrays are zero based)
It's not very efficient, but it's one line, and works well for normally sized arrays i.e. not a million indices
var idx = arr.length - 1 - arr.slice().reverse().findIndex( (o) => o.key == 'key' );
Show code snippet
var arr = [{key : 'not'}, {key : 'not'}, {key : 'key'}, {key : 'not'}];
var idx = arr.length - 1 - arr.slice().reverse().findIndex( (o) => o.key == 'key' ); // 2
console.log(idx)
Run code snippetEdit code snippet Hide Results Copy to answer Expand
A more efficient approach would be to iterate backwards until you find the object you're looking for, and break the loop
var arr = [{key: 'not'}, {key: 'not'}, {key: 'key'}, {key: 'not'}];
var idx = (function(key, i) {
for (i; i--;) {
if (Object.values(arr[i]).indexOf(key) !== -1) {
return i;
break;
}
} return -1;
})('key', arr.length);
console.log(idx)
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Videos
You can map into an array of booleans:
var lastIndex =sample.map(s =>
s.id === 0).lastIndexOf(true);
then access your array by last index:
console.log(sample[lastIndex]);
Array's lastIndexOf method compares searchElement to elements of the Array using strict equality (the same method used by the ===, or triple-equals, operator). If your array contains objects, then you have to use another method.
If performance is not important and the amount of data is not that big, you can use
const lastIndex = sample.length - 1 - sample
.slice()
.reverse()
.findIndex( item => item.id === 0 );
slice will create a copy of the array, reverse will reverse it, findIndex will return the first item that matches o.id === 0 and the final result is subtracted from sample.length - 1. It's not very efficient for a large data set.
Or you can use a plain for
function findLastIndexOf(arr) {
for (let i = arr.length; i--;) {
if (arr[i].id === 0) {
return i;
}
}
}
findLastIndexOf(sample);
for (let i = arr.length; i--;) looks weird but it will start iterating from the last position and stop when i reach the value of 0. Give it a try.
Hope it helps
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;
}
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')