Could anyone explain me a bit better a filter() + findIndex() example?

Hello, guys! It’s my very first topic here, I already apologize for the long text :sweat_smile:
I’m struggling so much with a part of my code that uses filter() and a findIndex() as its test function…
I don’t really need a solution/code for the problem! I just need to understand how it works… :confused:

Maybe it’d be easier to understand my issue with a brief context, so here it is:

I just created a simple “weather app” that searches for a city name and gets the current weather info from the “openweathermap” API. The app is already running perfectly as I want, by the way.
Every time I make a request (searching for a city name), the API responds me with an array with up to 5 cities related to the search, then I pick the right city and make a new request getting the current weather conditions and show it… Pretty simple.
But I don’t know why, the API has some repeated cities, these repeated objects have the same values for “name”, “country” and “state”, but slightly different latitude and longitude (even though they still point to the same location/city).
In my app, if there is more than 1 city found, it shows a list of them for the user to choose one. So there’s no need to show repeated cities in the list, of course :confused:
In order to fix this, I searched for a way to filter an array of objects that removes the repeated ones. I found the answer by chance in a StackOverflow comment (there were no explanations… it was just 2 lines of code). But I used it here, and it really solved my problem!! Perfect…

But I didn’t want to simply solve the problem, I’d like to understand how that really works… I know it’s nothing so complex…
Here it is:

let jsonLocations2 = jsonLocations.filter((element, index, array) => {
        return array.findIndex(item => item.name == element.name && item.country == element.country && item.state == element.state) == index;
    });

So, the jsonLocations2 will be a new array without the repeated cities.
I think I know how filter() and findIndex() work, I’ve seen examples and explanations (at MDN Web doc and W3Schools, for example), but I can’t understand how the iteration is happening here :frowning:

I uploaded here an example of repeated cities that I mentioned, and the filtered array right below.

So, let’s take this example:

jsonLocations = [
{city: 'aaaa', country: 'AA', state: 'aaaa'},
{city: 'aaaa', country: 'AA', state: 'aaaa'},
{city: 'aaaa', country: 'BB, state: 'bbb'},
{city: 'aaaa', country: 'CC', state: 'ccc'},
];

let jsonLocations2 = jsonLocations.filter((element, index, array) => {
        return array.findIndex(item => item.name == element.name && item.country == element.country && item.state == element.state) == index;
    });

On the first iteration of the filter() (element 0), it’s going to be returned true, since the first item checked of findIndex() will pass the conditions and it will be the same index as the current filter() element (0)… Good! Then findIndex() breaks and filter() goes to the second iteration.
On the second iteration of filter() (element 1), findIndex() starts checking the array again (item 0). It’ll be true for all the findIndex() conditions, but it won’t be returned to the filter() since the index found (0) is different from the index of the current element of filter (1). All good! So the findIndex() goes to check the next item of the array (1), correct?
And now the item (1) it’ll be true for all the conditions in findIndex() AND it’ll be also true for its index, because it’s the same as the current filter() index (1)!! Correct? So that element should be returned to the filter(), and go to the new array…? That’s how I understood it :confused: (but I’m wrong because it’s not what really happens…).
That’s all… I’m not sure about my explanation here, but I hope it makes sense!

Thanks for taking time to read and understand my long topic :pray:
If anyone could explain to me that process in a better way, I’d be very thankful!! :pray:

I think you got it right, with a bit confusion regarding when which function does what:

Just in case: the findIndex() doesn’t care about whether the return value matches the index of the current iteration.
findIndex() only looks for the item with the lowest index that matches all conditions and returns it (or -1 if no match found), after that the function is properly finished.

The comparision array.findIndex(...) == index is part of filter, at the time this gets evaluated findIndex is already finished. If this comparision is true (in other words, the element of the array is the first with this combination of city/country/state) the new array returned from filter will have this item included.

Keep in mind that especially if you don’t know how big that array is and with many duplicates, this would be a bad method, because in each filter iteration it tests a lot of elements that are potentially already uninteresting.

First initialize an empty array, then loop manually (or forEach ) over the array and only push those in the new array that are not yet in that new array (regarding your conditions), because this new array wouldn’t include duplicates it would be faster.
reduce could do it, too, for practice

1 Like

Ohh well noticed, @camperextraordinaire! It should be how you said, I’m sorry :sweat_smile:
It was just a mistake while creating this example here, the code was correct in my original app :blush:

@tzerio I’m very thankful for your explanation, that finally made sense to me :smiley:

I was really confusing when the array.findIndex() stops with the comparison for the filter()…

For example, on the second iteration of filter(), the findIndex() actually finds the item(0) that matches all conditions (so it returns that, and stops!) then the “== index” is not accepted, and the filter() moves on to next element… (I was considering that the filter() would continue… :man_facepalming: :sweat_smile:). Now it’s well understood!

And your point about the method is perfect, that would be bad in a big array… The way you suggested is much more interesting! I’m going to do it :blush:

Thank you so much again! :pray:

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.