I have a large data set of world cities (100K+ objects in an array), and a user's location information. I want to sort the array of world cities by the user's location, and return the top 7 (or however many) most relevant matches.
For example, if a user is located in Newport Beach, CA, USA, I want to sort the array where all "Newport Beach" cities in the United States show up first, with the user's exact location as the first result in the array.
The user's location comes in via an API, and is saved to local variables:
const userCity = "Newport Beach";
const userState = "CA";
const userCountry = "United States";
The original array is saved to a variable named allCities
. I sort this array as follows:
allCities
.sort((a, b) => b.city.startsWith(userCity) - a.city.startsWith(userCity))
.sort((a, b) => b.state && b.state.startsWith(userState) - a.state && a.state.startsWith(userState)) // This should only apply to the U.S.
.sort((a, b) => b.country.startsWith(userCountry) - a.country.startsWith(userCountry));
And the result, which I am slicing
to return only the first 7 matches:
const list = [
{ city: 'Newport', state: 'AR', country: 'United States' },
{ city: 'Chevy Chase Village',
state: 'MD',
country: 'United States' },
{ city: 'Newport', state: 'NH', country: 'United States' },
{ city: 'Newport', state: 'ME', country: 'United States' },
{ city: 'Newport', state: 'TN', country: 'United States' },
{ city: 'Newport', state: 'SC', country: 'United States' },
{ city: 'Newport', state: 'IN', country: 'United States' }
]
The problem here is that "Newport Beach" in "CA" doesn't even show up. The user should not have to type in the full name of their city. After just the first few characters, their location should be the first result, like an IntelliSense feature, since I already have their information.
I also don't know how Chevy Chase Village
in MD
showed up in the search results when "Newport Beach" was the search term.
The very first result should be the exact city, state (if they're in the U.S.), and country of the user. The second result should be the same city and country, but different states (again, if they're in the U.S.). Lastly, the same city, but other countries. If no matches are found, I will notify the user.
One thing to note is that I had a version of this that worked perfectly in the browser:
allCities
// Show closest matching cities first
.sort(location => userLocationData && location.city === userLocationData.city ? -1 : 1)
// Show exact matches based on city length
.sort((a, b) => a.city.length - b.city.length)
// Show closest matching countries
.sort(location => userLocationData && location.country === userLocationData.country ? -1 : 1)
// Show closest matching states
.sort(location => userLocationData && location.state === userLocationData.region ? -1 : 1);
The problem with this approach is that the allCities
array has with over 100K objects (over 8 MB), so it won't load on slow connections. It's also probably not ideal to load that much data on a device.
When I tried to use this logic in the server, it wouldn't run. I'm thinking it's because sort()
requires two arguments, and here, I'm providing only one. Other questions/answers provided on SO (like this one) were helpful, but none seemed to do the trick.
How can I sort this array based on user input to achieve the desired results?