Find last matching object in array of objects
JavascriptArraysJavascript Problem Overview
I have an array of objects. I need to get the object type ("shape" in this example) of the last object, remove it, and then find the index of the previous object in the array that has the same type, e.g. "shape".
var fruits = [ { shape: round, name: orange }, { shape: round, name: apple }, { shape: oblong, name: zucchini }, { shape: oblong, name: banana }, { shape: round, name: grapefruit }]
// What's the shape of the last fruit
var currentShape = fruits[fruits.length-1].shape;
// Remove last fruit
fruits.pop(); // grapefruit removed
// Find the index of the last round fruit
var previousInShapeType = fruits.lastIndexOf(currentShape);
// should find apple, index = 1
So, obviously the type in this example will be "round". But I'm not looking for an array value of "round". I'm looking for where fruits.shape = round.
var previousInShapeType = fruits.lastIndexOf(fruits.shape = currentShape);
But just using that doesn't work. I'm sure I'm missing something simple. How do I find the last item in the array where the shape of the object = round?
Javascript Solutions
Solution 1 - Javascript
var fruit = fruits.slice().reverse().find(fruit => fruit.shape === currentShape);
Solution 2 - Javascript
You can transform your array to an array boolean
type and get the last true
index.
const lastIndex = fruits.map(fruit =>
fruit.shape === currentShape).lastIndexOf(true);
Solution 3 - Javascript
var previousInShapeType, index = fruits.length - 1;
for ( ; index >= 0; index--) {
if (fruits[index].shape == currentShape) {
previousInShapeType = fruits[index];
break;
}
}
You can also loop backwards through array.
Fiddle: http://jsfiddle.net/vonn9xhm/
Solution 4 - Javascript
Using the Lodash library, you can find the last logical element.
_.findLast([1,2,3,5,4], n => n % 2 == 1); // Find last odd element
// expected output: 5
Solution 5 - Javascript
This is a solution that does not depend on reverse
, and therefore does not require "cloning" the original collection.
const lastShapeIndex = fruits.reduce((acc, fruit, index) => (
fruit.shape === currentShape ? index : acc
), -1);
Solution 6 - Javascript
Update - 27 October 2021 (Chrome 97+)
Proposal for Array.prototype.findLast
and Array.prototype.findLastIndex
is now on Stage 3!
Here's how you can use those:
const fruits = [
{ shape: 'round', name: 'orange' },
{ shape: 'round', name: 'apple' },
{ shape: 'oblong', name: 'zucchini' },
{ shape: 'oblong', name: 'banana' },
{ shape: 'round', name: 'grapefruit' }
]
let last_element = fruits.findLast((item) => item.shape === 'oblong');
// → { shape: oblong, name: banana }
let last_element_index = fruits.findLastIndex((item) => item.shape === 'oblong');
// → 3
You can read more in this V8 blog post.
You can find more in "New in Chrome" series.
Solution 7 - Javascript
An easier and relatively efficient solution. Filter and pop!
Filter all fruits matching the current shape and then pop to get the last one.
fruits.filter(({shape}) => shape === currentShape).pop()
var fruits = [{
shape: 'round',
name: 'orange'
}, {
shape: 'round',
name: 'apple'
}, {
shape: 'oblong',
name: 'zucchini'
}, {
shape: 'oblong',
name: 'banana'
}, {
shape: 'round',
name: 'grapefruit'
}];
// What's the shape of the last fruit
var currentShape = fruits[fruits.length - 1].shape;
// Remove last fruit
fruits.pop(); // grapefruit removed
alert(fruits.filter(({shape}) => shape === currentShape).pop().name);
Solution 8 - Javascript
plain JS:
var len = fruits.length, prev = false;
while(!prev && len--){
(fruits[len].shape == currentShape) && (prev = fruits[len]);
}
lodash:
_.findLast(fruits, 'shape', currentShape);
Solution 9 - Javascript
While the currently accepted answer will do the trick, the arrival of ES6 (ECMA2015) added the spread operator which makes it easy to duplicate your array (this will work fine for the fruit
array in your example but beware of nested arrays). You could also make use of the fact that the pop
method returns the removed element to make your code more concise. Hence you could achieve the desired result with the following 2 lines of code
const currentShape = fruits.pop().shape;
const previousInShapeType = [...fruits].reverse().find(
fruit => fruit.shape === currentShape
);
Solution 10 - Javascript
Based on Luke Liu's answer, but using ES6's spread operator to make it a bit easier to read:
const fruit = [...fruits].reverse().find(fruit => fruit.shape === currentShape);
Solution 11 - Javascript
Update - Array.prototype.findLast()
is now available for use
var fruits = [
{
shape: 'round',
name: 'orange'
},
{
shape: 'round',
name: 'apple'
},
{
shape: 'oblong',
name: 'zucchini'
},
{
shape: 'oblong',
name: 'banana'
},
{
shape: 'round',
name: 'grapefruit'
}
]
const last = fruits.findLast(n => n.shape === 'oblong');
console.log(last);
**Please check out browser compatibly before using it in this link
Read more about findLast
here
Another way to achieve this is using the reverse
(but less efficient)
var fruits = [
{
shape: 'round',
name: 'orange'
},
{
shape: 'round',
name: 'apple'
},
{
shape: 'oblong',
name: 'zucchini'
},
{
shape: 'oblong',
name: 'banana'
},
{
shape: 'round',
name: 'grapefruit'
}
]
const last = fruits.reverse().find(n => n.shape === 'oblong');
console.log(last);
Solution 12 - Javascript
I would suggest another nice solution which doesn't bother cloning a new object using reverse()
.
I use reduceRight
to does the job instead.
function findLastIndex(array, fn) {
if (!array) return -1;
if (!fn || typeof fn !== "function") throw `${fn} is not a function`;
return array.reduceRight((prev, currentValue, currentIndex) => {
if (prev > -1) return prev;
if (fn(currentValue, currentIndex)) return currentIndex;
return -1;
}, -1);
}
And usage
findLastIndex([1,2,3,4,5,6,7,5,4,2,1], (current, index) => current === 2); // return 9
findLastIndex([{id: 1},{id: 2},{id: 1}], (current, index) => current.id === 1); //return 2
Solution 13 - Javascript
You should use filter! filter takes a function as an argument, and returns a new array.
var roundFruits = fruits.filter(function(d) {
// d is each element of the original array
return d.shape == "round";
});
Now roundFruits will contain the elements of the original array for which the function returns true. Now if you want to know the original array indexes, never fear - you can use the function map. map also operates on an array, and takes a function which acts on the array. we can chain map and filter together as follows
var roundFruits = fruits.map(function(d, i) {
// d is each element, i is the index
d.i = i; // create index variable
return d;
}).filter(function(d) {
return d.shape == "round"
});
The resulting array will contain all objects in the original fruits array for which the shape is round, and their original index in the fruits array.
roundFruits = [
{
shape: round,
name: orange,
i: 0
},
{
shape: round,
name: apple,
i: 1
},
{
shape: round,
name: grapefruit
i: 4
}
]
Now you can do whatever you need to with the exact knowledge of the location of the relevant data.
// get last round element
fruits[4];
Solution 14 - Javascript
Here's a typescript version:
/**
* Returns the value of the last element in the array where predicate is true, and undefined
* otherwise. It's similar to the native find method, but searches in descending order.
* @param list the 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, find
* immediately returns that element value. Otherwise, find returns undefined.
*/
export function findLast<T>(
list: Array<T>,
predicate: (value: T, index: number, obj: T[]) => unknown
): T | undefined {
for (let index = list.length - 1; index >= 0; index--) {
let currentValue = list[index];
let predicateResult = predicate(currentValue, index, list);
if (predicateResult) {
return currentValue;
}
}
return undefined;
}
Usage:
const r = findLast([12, 43, 5436, 44, 4], v => v < 45);
console.log(r); // 4