Reputation: 57
So i just came accross a jquery live search script that uses regex to filter down and show the list items which matches the input text and hides rest.
The problem is, it matches phrases and in ascending order only.
Suppose if my list is:
<ul class="my_ul">
<li><a>Nvidia gtx 970 vs amd rx 390</a></li>
<li><a>Nvidia gtx 1060 vs nvidia gtx 970</a></li>
</ul>
Now if i enter: 1060 gtx nvidia970, it should return the second li, and ignore any extra spaces. (Means should match single words only in any order ignoring spaces)
For now i am using this script:
$("#search-input").keyup(function(){
// Retrieve the input field text and reset the count to zero
var filter = $(this).val(), count = 0;
// Loop through the comment list
$(".my_ul li").each(function(){
// If the list item does not contain the text phrase fade it out
if ($(this).text().search(new RegExp(filter, "i")) < 0) {
$(this).fadeOut(200);
// Show the list item if the phrase matches and increase the count by 1
} else {
$(this).show();
count++;
}
});
// Update the count
var numberItems = count;
$("#filter-count").text("Number of Comments = "+count);
});
But this only matches if the words are in order, For example: gtx 1060 vs nvidia gtx 970,
Here is jsfiddle: https://jsfiddle.net/xpvt214o/257804/
Please solve this for me, i am a beginner and just came accross regex so can't figure it out by replacing the regex using some of the codes here is stactoverflow. Please help, thanks :)
Upvotes: 2
Views: 1888
Reputation: 18950
The problem to solve here is separating numbers and letters in the filter
var.
Indeed, a regex comes handy to do so:
filter.match(/[a-zA-Z]+|[0-9]+/g).join("|")
This matches
either letters or numbers and joins
the resulting array to a string again with a regex alternation as the separator. This allows us to search for numbers and letters independently.
However, there's a problem: Since numbers and letters are separated you may get unexpected results, eg. search for Phantom90, both the line with 390 and Phantom9 will show up. To improve you could add an "exact phrase" search in quoted strings as used in search engines; or wrap the separated elements with a lookahead to filter strings that contain each element. The later approach is shown below:
Sample Code
$("#search-input").keyup(function(){
// Retrieve the input field text and reset the count to zero
var filter = $(this).val(), count = 0;
// Loop through the comment list
$(".my_ul li").each(function(){
var pattern = filter.trim().match(/[a-zA-Z]+|[0-9]+/g) || ""; // null coalescing: "" if null
if(pattern) {
pattern = pattern.map(function(el) {
return '(?=.*' + el + ')';
});
//join if there is something in the array
pattern = pattern.join("");
}
//console.log(pattern.join(""));
// If the list item does not contain the text phrase fade it out
if ($(this).text().search(new RegExp(pattern, "i")) < 0) {
$(this).fadeOut(200);
// Show the list item if the phrase matches and increase the count by 1
} else {
$(this).show();
count++;
}
});
// Update the count
var numberItems = count;
$("#filter-count").text("Number of Comments = "+count);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input placeholder="Search Me" id="search-input" type="text" />
<ul class="my_ul">
<li><a>Nvidia gtx 970 vs amd rx 390</a></li>
<li><a>Nvidia gtx 1060 vs nvidia gtx 970</a></li>
<li><a>Phaeton vs Phantom9</a></li>
</ul>
Remarks:
\d+|\D+
instead.some special characters in your input filter will break the script, escape them as shown here.
Upvotes: 1
Reputation: 402
If I understand your requirements, this is easy, you simply need a global flag, telling your regex to not return after the first match, than you apply a logic Or
to all your filters.
This is the "Regex-Solution" to your problem, since I don't know enough about JS, someone with more savvy with JS might be able to give you a more JS-centric solution.
You should end up with this result:
/(GTX|AVD|1060|970)/g
To archive that:
/(
.|
followed by your words till you have inputted all words./)
.g
to the flags you set in the constructor.See the regex in effect here:
const regex = /(GTX|AVD|1060|970)/g;
const str = `Bla AVD trd GTX 1060 df fggg`;
let m;
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
// The result can be accessed through the `m`-variable.
m.forEach((match, groupIndex) => {
if(groupIndex == 0) {
console.log(`Found match, group ${groupIndex}: ${match}`);
}
});
}
Upvotes: 0