9

I'm looking for a way to find the last index of an object in Javascript from a point in an array. For example:

array.lastIndexOf(object.key, start);

So far, I haven't found a good solution for this problem. I could splice the array from the 'start' point, reverse it, and then search the sliced array for the key value, but this seems like an inefficient solution to me.

EDIT:

To illustrate the problem a little more, I'm posting the code that I used in the end to solve the problem. Essentially; what I did was I used While to loop through the previous values in the array.

getLastValue = (index) => {
  const arr = [
    {'d':'01-02-2017','v':'123'},
    {'d':'02-02-2017'},
    {'d':'04-02-2017'},
    {'d':'05-02-2017','v':'456'},
    ...
  ];

  let lastValue;

  while (arr[index] && !arr[index].v) {
    index--;
  }

  lastValue = arr[index];

  return lastValue;
}
Xari
  • 266
  • 1
  • 4
  • 13
  • It's an array, right? And you're looking for the last instance? Is there a reason a simple `for(x = array.length - 1; x >= start; x--) {if (array[x] == object.key) return x; }` wouldn't work? ** note that I'm terrible with math, so it might be `x > start` – theGleep Sep 22 '17 at 14:42
  • 1
    Please add a [minimal, complete, and verifiable example](https://stackoverflow.com/help/mcve/) – Andreas Sep 22 '17 at 14:45
  • What exactly does not work about `lastIndexOf`? – Bergi Sep 22 '17 at 15:23
  • @Bergi it handles primitive types, not objects, see my proposed solution – Tom Siwik Oct 16 '18 at 15:31

7 Answers7

18

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
Tom Siwik
  • 992
  • 9
  • 22
  • Why you decrement constant variable in this loop? Is that even allowed in javascript? – BT101 Oct 22 '20 at 21:32
  • 1
    I don't know either codepen is not working correctly or you are not right with that: https://codepen.io/BT101/pen/wvWJWxO?editors=0010 – BT101 Oct 25 '20 at 23:54
  • Hmm. You are right. I'm wrong. I confused it with the behaviour of for..in loops and other languages. https://medium.com/@mautayro/es6-variable-declaration-for-loops-why-const-works-in-a-for-in-loop-but-not-in-a-normal-a200cc5467c2 I've adjusted my answer. Thanks for pointing it out. I also deleted my comment to not cause any confusion. I suppose I was mutating the variable with i-- – Tom Siwik Oct 26 '20 at 07:42
4

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' );

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)

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)
adeneo
  • 312,895
  • 29
  • 395
  • 388
  • This is a great solution. I ended up using a modified version of your extended approach. I will modify the original question to show a fuller version of what my problem was, and how I modified your for loop approach. Thanks for your help! – Xari Sep 26 '17 at 14:13
  • Your one-liner returns `array.length` when key is not found instead of the expected `-1` – Tom Siwik Oct 23 '18 at 14:20
3

I think you want something like below:

var arr = [ { key: "a" }, { key: "b" }, { key: "c" }, { key: "e" }, { key: "e" }, { key: "f" } ];

console.log(lastIndexOf("e", 2));

function lastIndexOf(keyValue, start) {
    for (var i = arr.length - 1; i >= start; i--) {
        if (arr[i].key === keyValue) {
            return i;
        }
    }
    return null;
}
Faly
  • 13,291
  • 2
  • 19
  • 37
  • 1
    Hey; thanks Faly. You were right; exactly what I needed. I posted my full solution in the question block above. Unfortunately; I had to give the answer to the user who responded apparently just seconds before you. I appreciate your help though. – Xari Sep 26 '17 at 14:32
2

You can do this:

  1. reverse your array: let newArr = arr.reverse()
  2. use findIndex: newArr.findIndex(obj => obj.d == "your String")
xiawi
  • 1,772
  • 4
  • 19
  • 21
  • and don't forget to do more calc if you want true index in the non-reversed array: array.length -array.reverse().findIndex(item => item.type === 'some_string') - 1; – deathfry Oct 21 '22 at 13:02
1

i used sample code, like this:

//find last index in object array
const lastIndexOf = arr.reduce((acc,cur,idx)=>cur.key==xxx?idx:acc,-1)

//find last index of object in object array
const lastIndexOfObject = arr.reduce((acc,cur,idx)=>cur.key==xxx?cur:acc,undefined)
liujigang
  • 521
  • 6
  • 6
1
let a = [
        {prop1:"abc",prop2:"incomplete"},
        {prop1:"bnmb",prop2:"completed"},
        {prop1:"bnmb",prop2:"prajapati"},
        {prop1:"zxvz",prop2:"testAJ"},
        {prop1:"last",prop2:"completed"},
        {prop1:"last",prop2:"incomplete"},
        {prop1:"last",prop2:"incomplete11"},
        {prop1:"last",prop2:"incomplete"},
        {prop1:"last",prop2:"incomplete"},
        {prop1:"last",prop2:"incompleteness"},
      ];

      let findLastduplicateObjectIndex =  a.map(el => el.prop2).lastIndexOf("incomplete"); 
0

Just try to find last index in the whole massive and compare it to start

let ind = array.lastIndexOf(object.key); 
if (ind > start) {return}
Redair
  • 204
  • 1
  • 2
  • 12
  • 2
    This won't work, lastIndexOf can only handle primitive types (strings, numbers, boolean): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf – Tom Siwik Oct 16 '18 at 15:23