So I'm creating a web app using react with typescript that calls an api endpoint (it's a yugioh card database https://db.ygoprodeck.com/api-guide/). I should probably also point out that I'm using RTK Query, it might be a caching things I don't know. Specifically for my problem, it's a query that makes a fuzzy name search to see if a user's input text matches any card in the database. The returned array from the api however is not sorted in a way that I desire, so I wanted to fix that with a utility function as I can see myself sorting this way in multiple places.
Given the search "Dark" for example, the api returns all cards where dark appears in the name, regardless of when in the name the word appear. A card could for example be called "Angel of Dark" and it would come before a much more likely search "Dark Magician" because the api returns it in alphabetical order. But I want it to be sorted in a way that cards that starts with the word appears before cards where the word appears later.
I think I've done it in a way that should return it correctly, but alas it doesn't, so I figured the experts could maybe tell me where the code is wrong, if it's in the utility function or if it's with RTKQ or somewhere else where I'm just completely wrong.
Here's the utility function:
import { CardData } from "../models/cardData.interface";
const SortNameBySearch: Function = (search: string, cardData: CardData) => {
const sortedData = [...cardData.data];
sortedData.sort((a, b) => {
// Sort results by matching name with search position in name
if (
a.name.toLowerCase().indexOf(search.toLowerCase()) >
b.name.toLowerCase().indexOf(search.toLowerCase())
) {
return 1;
} else if (
a.name.toLowerCase().indexOf(search.toLowerCase()) <
b.name.toLowerCase().indexOf(search.toLowerCase())
) {
return -1;
} else {
if (a.name > b.name) return 1;
else return -1;
}
});
return sortedData;
};
export default SortNameBySearch;
I've sliced the array to only show the top 3 results and it looks like this, but it's weird because the first one should't even be in this array as it doesn't contain the searched name, which makes me think it might be a caching thing with RTKQ but I'm honestly clueless as how to fix that.
For good measure and better context here's how I'm using it in context
import { FC, useEffect, useRef, useState } from "react";
import { useGetCardsBySearchQuery } from "../../../../services/api";
import { SearchResultsProps } from "./SearchResults.types";
import SortNameBySearch from "../../../../utils/SortNamesBySearch";
import { Card } from "../../../../models/card.interface";
const SearchResults: FC<SearchResultsProps> = ({ search }) => {
const [searched, setSearched] = useState("");
const [typing, setTyping] = useState(false);
const searchRef = useRef(0);
useEffect(() => {
clearTimeout(searchRef.current);
setTyping(true);
if (search !== null) {
searchRef.current = window.setTimeout(() => {
setSearched(search);
setTyping(false);
}, 100);
}
}, [search]);
const { data, isLoading, isError, isSuccess } = useGetCardsBySearchQuery(
searched,
{ skip: typing }
);
const results =
!typing && isSuccess
? SortNameBySearch(searched, data)
.slice(0, 3)
.map((card: Card) => {
return (
<p key={card.id} className="text-white">
{card.name}
</p>
);
})
: null;
if (isError) {
return <div>No card matching your query was found in the database.</div>;
}
return (
<>
{isLoading ? (
"loading..."
) : typing ? (
"loading..."
) : (
<div className="bg-black relative">{results}</div>
)}
</>
);
};
export default SearchResults;
Given that I'm still new to this whole thing there are likely other issues with my code and of course I want to improve, so if there's something in there that just doesn't make sense please do let me know.
Looking forward to see your answers. -Benjamin