Find the lastIndexOf() an object with a key in array of objects
Asked Answered
R

7

11

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;
}
Retriever answered 22/9, 2017 at 14:38 Comment(4)
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 > startTsana
Please add a minimal, complete, and verifiable exampleSlub
What exactly does not work about lastIndexOf?Heelandtoe
@Heelandtoe it handles primitive types, not objects, see my proposed solutionMalamut
M
19

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
Malamut answered 16/10, 2018 at 15:47 Comment(4)
Why you decrement constant variable in this loop? Is that even allowed in javascript?Literally
I don't know either codepen is not working correctly or you are not right with that: codepen.io/BT101/pen/wvWJWxO?editors=0010Literally
Hmm. You are right. I'm wrong. I confused it with the behaviour of for..in loops and other languages. medium.com/@mautayro/… 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--Malamut
A possible modification of this is to map to an array of boolean values: arr.map(el => el.key === 'e').lastIndexOf(true); This allows for more complex logic based on multiple conditions: el.key === 'e' && el.otherProperty === 'somethingElse';Erine
O
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)
Ornithine answered 22/9, 2017 at 14:48 Comment(2)
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!Retriever
Your one-liner returns array.length when key is not found instead of the expected -1Malamut
K
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;
}
Keown answered 22/9, 2017 at 14:48 Comment(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.Retriever
T
2

You can do this:

  1. reverse your array: let newArr = arr.reverse()
  2. use findIndex: newArr.findIndex(obj => obj.d == "your String")
Trattoria answered 17/5, 2020 at 7:6 Comment(1)
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;Loveland
B
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)
Bothy answered 29/6, 2021 at 12:30 Comment(0)
K
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"); 
Kirovabad answered 14/10, 2022 at 11:36 Comment(0)
A
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}
Axilla answered 22/9, 2017 at 14:45 Comment(1)
This won't work, lastIndexOf can only handle primitive types (strings, numbers, boolean): developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Malamut

© 2022 - 2024 — McMap. All rights reserved.