Reputation: 873
I'm trying to implement a search of an object with multiple key/value pairs that can be either string
or number
.
This is what I'm currently using (simplified, of course):
const input = 'Hello WORld 21'; // this could also be of type number
const search = 'O w'; // this could also be of type number
let result = true;
let startTime, endTime;
const t0 = performance.now();
for (let i = 0; i < 1000000; i++) {
let parsedInput = JSON.stringify(input).toLowerCase().replace(/(^"|"$)/g, '');
let parsedSearch = JSON.stringify(search).toLowerCase().replace(/(^"|"$)/g, '');
result = parsedInput.indexOf(parsedSearch);
}
const t1 = performance.now();
console.log(`Call to doSomething took ${t1 - t0} milliseconds with result = ${result}`);
// ~393ms on a Ryzen 5600X
So this works, but seems to be expensive, especially when I go through tens of thousands of objects (or even more). So I wonder if there is a more elegant way to implement a search like this.
Thank you in advance!
Edit: Based on Sergio J's answer, here is what I got now:
const t2 = performance.now();
let i, s;
for (let j = 0; j < 1000000; j++) {
i = isNaN(input) ? input.toLowerCase() : input.toString().toLowerCase();
s = isNaN(search) ? search.toLowerCase() : search.toString().toLowerCase();
result = i.indexOf(s);
}
const t3 = performance.now();
console.log(`Call to doSomething took ${t3 - t2} milliseconds with result = ${result}`);
// ~66ms on a Ryzen 5600X
Upvotes: 1
Views: 533
Reputation: 61
you may not need that Json.stringify, just toString() in the cases where the input is a number (check with isNaN()).
There are some post here talking about the efficiency of different methods.
JavaScript: indexOf vs. Match when Searching Strings? check the link to find the most suitable for your needs and users.
Here is a code snippet with a indexOf version, extracted in a function, that checks input and search values to convert them to string if necessary, and tells you what has been found.
function stringFinderIndexOf(input, search) {
let isNumber = !isNaN(input);
if (isNumber) {
input = input.toString();
}
let isSearchANumber = !isNaN(search);
if (isSearchANumber) {
search = search.toString();
}
let foundElement = input.toLowerCase().indexOf(search.toLowerCase()) !== -1;
if (foundElement) {
let isNumberText = isNumber ? "a numeric value" : "a text";
console.log("found with indexOf in " + isNumberText);
}
}
Of course, if you have as I understand an object, and you want to loop through, you could use Object.values() and a for loop.
function stringInObjectFinder(input, search) {
let values = Object.values(input)
for (const key in values) {
stringFinderIndexOf(key, search);
}
}
Find full code in a sandbox with a couple of functions using other methods as string.match(regex).
https://codesandbox.io/s/focused-chatterjee-gqijlr?file=/src/index.js
-------- Addition --------
Edit: as author suggested cleaner if, it is always a good idea to extract the if as a method with an explanatory name. In this case was left in order to put all code in a function to explain myself.
You can see in the function duplicated code already (smells).
function transformToStringIfNecessary(input) {
let isInputANumber = !isNaN(input);
if (isInputANumber) {
input = input.toString();
}
return input;
}
function containsInputTheSearchValue(input, search) {
return input.toLowerCase().indexOf(search.toLowerCase()) !== -1;
}
function stringFinderIndexOf(input, search) {
let isNumber = !isNaN(input);
input = transformToStringIfNecessary(input);
search = transformToStringIfNecessary(search);
let parsedInput = containsInputTheSearchValue(input, search);
if (parsedInput) {
let isNumberText = isNumber ? "a numeric value" : "a text";
console.log("found with indexOf in " + isNumberText);
}
}
You could clean the code even more, but guess you got the point. Hope it helps.
Upvotes: 3