Regex for search, a more refined fuzzy search - javascript

I have the generic fuzzy search right now, so that if type "a" it'll return me every word that contains an A. However I want a more refined search that does this.
For example I have the word: 'Frankster Muniz'
searching "frank","fr","f", "mun", "m" will indeed return 'Frankster Muniz'
searching "rank","ster","r", "niz" will not return anything
it must match the first beginning words,I have no idea where to start here. I am very new to regex. Thank you

You could begin with this:
If input is user-input and name is name on database ,
var name = "Frankster Muniz";
var input = "Frank";
function match(name,input){
var matched = 0;
if(name.match(new RegExp("\\b"+input+".*","i")))
matched=1;
return matched;
}
console.log(match(name,"Frank"));
console.log(match(name,"ster"));

You can start matching at the beginning of words by using the word boundary character \b. So the power of the regex becomes:
new RegExp(`\\b${input}`, 'i');
Check out the interactive example:
let names = [
'Frankster Muniz',
'Sarah Walin',
'Tyler Robinson',
'Ronald Muniz'
];
let results = document.querySelector('#results');
let input = document.querySelector('input');
input.addEventListener('input', function() {
findUsers(this.value);
});
buildList(names);
function findUsers(input) {
let reg = new RegExp(`\\b${input}`, 'i');
let found = names.filter(name => reg.test(name));
buildList(found);
}
function buildList(names) {
let html = '';
names.forEach(name => html += `<li>${name}</li>`);
results.innerHTML = html;
}
<label>Filter:</label>
<input id="search">
<br><br>
<div>Names:</div>
<ul id="results"></ul>

Related

Regex Not As Expected [duplicate]

This question already has an answer here:
Escape string for use in Javascript regex [duplicate]
(1 answer)
Closed 17 days ago.
In my web page I have a "find" feature, where the user types in some text in "find" dialog and it gets the index of where the text can be found in a variable. In advance I know neither the text that will be searched, nor the text the user will be looking for. I provide them an option for whether to match case, and whether to find only whole words (e.g. whether "managers" should be a match if they enter "manager".
The following code is a stripped down version of my code with just what is relevant to my issue. In this example, I want to look for "(1)" in "This is a test.(10)". My expectation after execution is that index will be -1, but it is 16, which is the index of the "1".
Can you tell me why this doesn't work and what would work? Thanks.
let matchCase = false;
let wholeWord = false;
let index = 0;
let options = "";
let phrase = "(1)";
phrase = phrase.replaceAll(String.fromCharCode(160), "\\s");
if (!matchCase) options += "i";
if (wholeWord) phrase = "\\b" + phrase + "\\b";
let regex = new RegExp(phrase, options);
let text = "This is a test.(10)";
let index = text.search(regex);
console.log(index);
You need to escape regular expression metacharacters like ( in the input.
//Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
let matchCase = false;
let wholeWord = false;
let options = "";
let phrase = escapeRegExp("(1)");
phrase = phrase.replaceAll(String.fromCharCode(160), "\\s");
if (!matchCase) options += "i";
if (wholeWord) phrase = "\\b" + phrase + "\\b";
let regex = new RegExp(phrase, options);
let text = "This is a test.(10)";
let index = text.search(regex);
console.log(index);

Noob Syntax help: How to global replace characters in a string using an indexed substring e.g. text[i]?

In this problem, I would like to find the number of distinct characters (case-insensitive).
I understand there are other approaches to the problem, but my current approach is to identify the first character in the text string and remove all equivalent characters globally, +1 to the counter and rinse and repeat for all remaining chars. Looking at the JS docs online, I can't seem to figure out how to make this work, I figured someone could teach me how to do the regular expression for this. Thanks.
let text = "aAabbbacccaaade";
text = text.replace(text[0]/gi,"");
// text = text.replace(/text[0]/gi,""); => aAabbbacccaaade
// text = text.replace(text[0]/gi,""); => gi is undefined error.
Create your regex using the new constructor and a string argument:
let text = "aAabbbacccaaade";
console.log(text);
let regex = new RegExp(text[0], "gi");
text = text.replace(regex,"");
console.log(text);
Looping this should work for your method:
const text = "aAabbbacccaaade";
function countUnique(str) {
let mutableStr = str;
let count = 0;
while (mutableStr.length > 0) {
let r = new RegExp(mutableStr[0], "gi");
mutableStr = mutableStr.replace(r, "");
count++;
}
return count;
}
console.log(countUnique(text));

Filter a JavaScript array of strings matching a sequence of characters

How can I efficiently filter an array of strings matching a sequence of characters, such that characters may be matched anywhere in the string but in the order they are used?
It's a feature commonly seen in editors and IDEs to quickly filter filenames.
See an illustration of the filter in action in the attached image link.
This is not a duplicate of JavaScript autocomplete without external library, because one of the requirements here is for user input "Caz" to match "Cangzhou" which is explained in the answer to this question, but not in the answers to other questions.
This is actually simpler than it looks. Also, the currently accepted solution does not actually work consistently.
You simply need to construct a regex that accepts 0 or more characters between each character of the search string.
const values = ['Belgium', 'Brest', 'Britian']
const query = 'Be'
// /.*b.*e.*/
const re = RegExp(`.*${query.toLowerCase().split('').join('.*')}.*`)
// [ 'Belgium', 'Brest' ]
const matches = values.filter(v => v.toLowerCase().match(re))
I think this should be pretty close. The answer tries to build a regular expression to make it so that characters are matched in the order they appear in the search term.
const values = ['Brussels', 'Cairo', 'Casablanca', 'Cangzhou', 'Caracas',
'Los Angeles', 'Osaka'];
const match = (s) => {
const p = Array.from(s).reduce((a, v, i) => `${a}[^${s.substr(i)}]*?${v}`, '');
const re = RegExp(p);
return values.filter(v => v.match(re));
};
console.log(match('Ca')); // Cairo, Casablanca, Cangzhou, Caracas
console.log(match('Caz')); // Cangzhou
console.log(match('as')); // Casablanca, Caracas
console.log(match('aa')); // Casablanca, Caracas, Osaka
Here is an example for that.
To reduce error unexpected, you should limit the input value to what you want they key in.
var all = ['test','string','array','example'];
function getMatch(arr, str){
var reg = new RegExp(str.split('').join('.*'), 'i');
return arr.filter(function(item) {
if (item.match(reg)) {
return item;
}
});
}
function search(value){
document.getElementById("result").innerHTML = getMatch(all, value);
}
<input type="text" onkeyup="search(this.value)">
<br />
<span id="result"></span>
This simple example uses a Regular Expression and Array.prototype.filter()
var strings = ["Cairo", "Casablanca", "Cangzhou", "Carcas"]; // Your array of things to filter
function disp(matches) {
// Displays the filtered results on the page
var results = document.getElementById("suggestions");
results.innerHTML = matches.join("<br>");
}
function regExpTest(inputString, regex) {
// Checks to see if what the user typed matches any items in gucciArray
return regex.test(inputString);
}
function typeKey(event) { // This function is supposed to be called whenever the user types a letter in the text box
var textTyped = event.target.value; // What the user typed in the text box;
var filter = new RegExp(textTyped.split('').join('.*'), 'i'); // Matches anything with the characters that the user typed, in order, with any characters inbetween them
// Filter the array
// Returns a new array containing the items from the old array that pass the RegExp test
var filteredArray = strings.filter(s => regExpTest(s, filter));
// Display the filtered results on the page
disp(filteredArray);
}
disp(strings);
<html>
<head>
</head>
<body>
Type here: <input type="text" onkeyup="typeKey(event);">
<br>
List of matches:
<div id="suggestions"></div>
</body>
</html>
disp is optional. It's just for the interactive code snippet. If you want to get the values to use later, replace the last line in typeKey with return newArray;

Search Array for strings and return matches^

I have an array with names of employees such as.
var names = ['Jordan,Michael','Davis,Jordan','Franco,James','Rogen,Seth','Griffin,Peter','James,Tim',..]
I would like to add a feature to my webpage to allow the user to type in the name they are looking for and be able to see everyone with that name. So if the user typed in Jordan. Both Jordan,Michael and Davis,Jordan would return.
Is there a way that I can do this with javascript/jquery?
Use Array.prototype.filter
var search = 'Jordan';
var names = ['Jordan,Michael','Davis,Jordan','Franco,James','Rogen,Seth','Griffin,Peter','James,Tim']
var results = names.filter(name => name.indexOf(search) > -1);
console.log(results);
For case in-sensitive match, you need to use regex.
The following code snippet assumes that there are only alpha-numeric characters in both needle and haystack.The special characters can be removed by using a simple replace, str = str.replace(/[^a-zA-Z0-9]/g, '');
Note: To allow special characters, they need to be escaped.
var names = ['Jordan,Michael','Davis,Jordan','Franco,James','Rogen,Seth','Griffin,Peter','James,Tim']
var search = 'jorDan' // cAsE-InsEnsItIvE
regex = new RegExp(search, 'i');
var results = names.filter(name => regex.test(name));
console.log(results);
document.body.innerHTML = '<pre>' + JSON.stringify(results, 0, 4) + '</pre>';
Try having an array of objects like this:
var names = [
{firstName:'Jordan', lastName:'Michael'},
{firstName:'Davis', lastName:'Jordan'},
]
var searchedName = "Jordan";
var result = names.filter(function( searchedName) {
return names.firstName == searchedName || names.lastName == searchedName;
});
This is a fuzzy search field, I couldn't think of any names so this one has three Tims to illustrate the point.
It uses a Regular expression to match the letters currently being typed into the input field.
var names = [
'Jordan,Michael',
'Davis,Jordan',
'Franco,James',
'Rogen,Seth',
'Griffin,Peter',
'James,Tim',
'Burton,Tim',
'Allen,Tim'
];
var
input = document.querySelector('#input'),
results = document.querySelector('#results');
input.addEventListener('keyup', function(e) {
filterNames( this.value );
});
// init the filter;
filterNames('');
// helper to filter the names array by a value
function filterNames( value ){
results.innerHTML = names.filter(function(name) {
return name.match(new RegExp( value.split('').join('.*'), 'ig'));
}).reduce(function( str, name) {
return str + '<li>' + name + '</li>';
}, '');
}
<input type="text" id="input">
<ul id="results"></ul>

Regex to capture Ids from text

I have the following regex where I am trying to capture the Ids of each start comment. But for some reason I am only able to capture the first one. It won't grab the Id of the nested comment. It only prints 1000 to the console. I am trying to get it to capture both 1000 and 2000. Can anyone spot the error in my regex?
<script type="text/javascript">
function ExtractText() {
var regex = /\<!--Start([0-9]{4})-->([\s\S]*?)<!--End[0-9]{4}-->/gm;
var match;
while (match = regex.exec($("#myHtml").html())) {
console.log(match[1]);
}
}
</script>
<div id="myHtml">
<!--Start1000-->Text on<!--Start2000-->the left<!--End1000-->Text on the right<!--End2000-->
</div>
Based on Mike Samuel's answer I updated my JS to the following:
function GetAllIds() {
var regex = /<!--Start([0-9]{4})-->([\s\S]*?)<!--End\1-->/g;
var text = $("#myHtml").html();
var match;
while (regex.test(text)) {
text = text.replace(
regex,
function (_, id, content) {
console.log(id);
return content;
});
}
}
In
<!--Start1000-->Text on<!--Start2000-->the left<!--End1000-->Text on the right<!--End2000-->
the "1000" region overlaps the "2000" region, but the exec loop only finds non-overlapping matches since each call to exec with the same regex and string starts at the end of the last match. To solve this problem, try
var regex = /<!--Start([0-9]{4})-->([\s\S]*?)<!--End\1-->/g;
for (var s = $("#myHtml").html(), sWithoutComment;
// Keep going until we fail to replace a comment bracketed chunk
// with the chunk minus comments.
true;
s = sWithoutComment) {
// Replace one group of non-overlapping comment pairs.
sWithoutComment = s.replace(
regex,
function (_, id, content) {
console.log(id);
// Replace the whole thing with the body.
return content;
});
if (s === sWithoutComment) { break; }
}
You can use grouping and then another regexp:
var regex = /(<!--Start)([0-9]{4})/ig;
var str = document.getElementById('myHtml').innerHTML;
var matches = str.match(regex);
for(var i=0;i<matches.length;i++){
var m = matches[i];
var num = m.match(/(\d+)/)[1];
console.log(num);
}

Categories

Resources