jQuery Autocomplete: How to match entire/full words from array - javascript

I have an autocomplete that has multiple categories with values that matches any set of letters from the users input. Each category value has multiple strings stored in it (see value.application sample data below).
This works fine, but I want it to only match entire words intead of letter combination. How can this be achieved?
Example:
Source Data: "all caller tallest"
User Enters: "all"
Returns: "all caller tallest"
Looking for it to only return: "all"
source: function(request, response) {
var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i"); // match letters in string
response($.grep(SearchList, function(value) {
return matcher.test(value.label) ||
matcher.test(value.value) ||
matcher.test(value.sku) ||
matcher.test(value.application) ||
matcher.test(value.discontinuedproductlist) ||
matcher.test(value.type);
}));
},
As mentioned, each of these values, has multiple strings of words within them.
Example of (value.application) data: "dry wet cold warm hot burned charred dirty clean soiled"

Was able to make this work by adding the "\b" regex property to each side of the search term (see matcherWords).
Now i can return letters combination matches for some of the categories, and only whole word matches for the others. Hopefully this helps someone else.
source: function(request, response) {
var matcherLetters = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i"); // match letters in string
var matcherWords = new RegExp("\\b" + $.ui.autocomplete.escapeRegex(request.term) + "\\b", "i"); // match words in string
response($.grep(SearchList, function(value) {
return matcherLetters.test(value.label) ||
matcherLetters.test(value.value) ||
matcherWords.test(value.sku) ||
matcherWords.test(value.application) ||
matcherWords.test(value.discontinuedproductlist) ||
matcherWords.test(value.type);
}));
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Related

How can i check for only one occurence only of each provided symbol?

I have a provided array of symbols, which can be different. For instance, like this - ['#']. One occurrence of each symbol is a mandatory. But in a string there can be only one of each provided sign.
Now I do like this:
const regex = new RegExp(`^\\w+[${validatedSymbols.join()}]\\w+$`);
But it also returns an error on signs like '=' and so on. For example:
/^\w+[#]\w+$/.test('string#=string') // false
So, the result I expect:
'string#string' - ok
'string##string - not ok
Using a complex regex is most likely not the best solution. I think you would be better of creating a validation function.
In this function you can find all occurrence of the provided symbols in string. Then return false if no occurrences are found, or if the list of occurrences contains duplicate entries.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
const escapeRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
function validate(string, symbols) {
if (symbols.length == 0) {
throw new Error("at least one symbol must be provided in the symbols array");
}
const symbolRegex = new RegExp(symbols.map(escapeRegExp).join("|"), "g");
const symbolsInString = string.match(symbolRegex); // <- null if no match
// string must at least contain 1 occurrence of any symbol
if (!symbolsInString) return false;
// symbols may only occur once
const hasDuplicateSymbols = symbolsInString.length != new Set(symbolsInString).size;
return !hasDuplicateSymbols;
}
const validatedSymbols = ["#", "="];
const strings = [
"string!*string", // invalid (doesn't have "#" nor "=")
"string#!string", // valid
"string#=string", // valid
"string##string", // invalid (max 1 occurance per symbol)
];
console.log("validatedSymbols", "=", JSON.stringify(validatedSymbols));
for (const string of strings) {
const isValid = validate(string, validatedSymbols);
console.log(JSON.stringify(string), "//=>", isValid);
}
I think you are looking for the following:
const regex = new RegExp(`^\\w+[${validatedSymbols.join()}]?\\w+$`);
The question mark means 1 or 0 of the previous group.
You might also need to escape the symbols in validatedSymbols as some symbols have a different meaning in regex
Edit:
For mandatory symbols it would be easier to add a group per symbol:
^\w+(#\w*){1}(#\w*){1}\w+$
Where the group is:
(#\w*){1}

Search mechanism to include words as a whole [duplicate]

This question already has answers here:
Regex match entire words only
(7 answers)
Closed 4 years ago.
I have created a search mechanism that searches through an array of strings for an exact string match, however I want it to be a bit more intuitive.
I can also get it to search for a string within the string (for example chicken in grilled chicken - however the issue is this allows users to type ken or ill and it returns grilled chicken.
I would like it to return if I typed in chicken or grilled.
Does anyone have any suggestions on how to have a more intuitive search mechanism?
EDIT:
The correct answer below worked when typing 1 word and it would search all individual words in a string. However, I realised it fails when you search with 2 words (as it only searches each string word individually).
I solved this by adding || search == string to the if to include not just individually word matches but whole string matches.
However I am still having an issue with it either searching for:
Whole string matches
OR
Matches with individual words.
This means it fails when search = green cup and string = big green cup. Is there a way to solve this by cutting for collections to search within? Perhaps something similar to:
string.split(' ') but to also include big green, green cup to the array also?
Try This Simplest Code without Regex
var data = ["first string1 is here", "second string2 is here", "third string3 is here"];
var wordToSearch = "string2 is thanks";
var broken = wordToSearch.split(' ');
var result = 'not found';
if(broken.length == 1){
data.forEach(function(d){
d1 = d.split(' ');
if(d1.includes(wordToSearch))
result = d;
});
}
else if(broken.length == 2)
{
data.forEach(function(d){
var d1 = d.split(' ');
if(d1.includes(broken[0]) && d1.includes(broken[1]))
{
result = d;
}
});
}
alert(result);
I'd use RegExp with word boundary anchor - \b.
function search(query, arr) {
var res = [];
var re = new RegExp('\\b' + query + '\\b');
arr.forEach(function (item) {
if (re.test(item)) res.push(item);
});
return res;
}
It sounds like you only want to search by whole words, if that's the case, you could split the string by the space character and then search through the resultant array for matches.

RegExp case insensitive search for whole word with variable

I have an object with strings properties I want to compare to multiple user inputs using case insensitivity. My goal is to match input strings to object strings to increment the associated value by 1 (if it's a match).
var objArr = [
{"O": 0},
{"foo": 0},
{"faa": 0},
{"A": 0}
];
Everything is working smoothly except for the case insensitivity. The RegExp method I used just looks for one letter instead of the whole word. I'm probably not using the right syntax, but I can't find results on google which explain the /i flag along with a variable.
My closest try was :
var re = new RegExp(b, "i"); //problem here
if (allinputs[i].value.match(re)) { //and here
This code indeed allows case insensitivity but it doesn't look for the whole object property string and stops for letters. For exemple typing "foo" will result in a match to "O" because it contains the letter "O", and the property "O" is before "foo". Accordingly, typing "faa" matches to "faa" and not "A", because "faa" is before "A" in the objects array. Strings that don't exist in my object like "asfo" will still be matched to "O" because of the one common letter.
Is there a way to search for the whole property string with case insensivity using the regExp /i flag ? I want to avoid using .toUpperCase() or .toLowerCase() methods if possible.
Fiddle here : https://jsfiddle.net/Lau1989/b39Luhcu/
Thanks for your help
To check that a regex matches the entire string, you can use the assert beginning character (^) and assert end ($).
For example, hello matches e but not ^e$.
For your code, just prepend ^ to the regex and append $:
var re = new RegExp("^" + b + "$", "i");
fiddle
Edit: Some characters have special meanings in regexes (^, $, \, ., *, etc). If you need to use any of these characters, they should be escaped with a \. To do this, you can use this simple replace:
str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
So, your regex will end up being
new RegExp("^" + b.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&") + "$", "i");
See this question for more about escaping a regex.
You could also just convert the two strings to lowercase and then compare them directly. This will allow you to use special characters as well.
if (stringA.toLowerCase() == stringB.toLowerCase())) {
...
}
Your approach was almost right, but you need limitate your regular expression to avoid an any match using ^ (start of string) and $ (end of string).
Here is a code that I made that may fit to your need:
function process()
{
var allinputs = document.querySelectorAll('input[type="text"]');
var list = new Array();
var input = "";
objArr.map(function(value, index, array){ list.push(Object.keys(value))})
for(var i = 0; i < allinputs.length; i++)
{
input = allinputs[i];
if(input.value)
{
list.map(function( item, index, array ) {
var re = new RegExp("^"+input.value+"$", "i");
if(item.toString().match(re))
{
allinputs[i].value = "1";
objArr[index][item] += 1;
allinputs[i].style.backgroundColor = "lime";
document.getElementById('output').innerHTML += item + " : " + objArr[index][item] + "<br />";
}
});
}
}
}
The first thing here is create a list of keys from your objArr, so we can access the key names easily to match with what you type
objArr.map(function(value, index, array){ list.push(Object.keys(value))})
Then the logic stills the same as you already did, a for loop in all inputs. The difference is that the match will occur on list array instead of the objArr. As the index sequence of list and objArr are the same, it's possible to access the object value to increment.
I used the .map() function in the list array, bit it's also possible use a for loop if you prefer, both method will work.
I hope this help you!

javascript regular expression matching cityname

I have the following array of data named cityList:
var cityList = [
"Anaa, French Polynesia (AAA)",
"Arrabury, Australia (AAB)",
"Al Arish, Egypt (AAC)",
"Ad-Dabbah, Sudan (AAD)",
"Annaba, Algeria (AAE)",
"Apalachicola, United States (AAF)",
"Arapoti, Brazil (AAG)",
"Aachen, Germany (AAH)",
"Arraias, Brazil (AAI)",
"Awaradam, Suriname (AAJ)",
"Aranuka, Kiribati (AAK)",
"Aalborg, Denmark (AAL)"
];
I want to first search the city name starting at the beginning of the string.
Next I want to search the code portion of the string: AAA, AAB, AAC, etc...
I want to apply a search pattern as a javascript regular expression, first to the city name, and second to the city code.
Here are my regular expressions:
// this regular expression used for search city name
var matcher = new RegExp("^" + re, "i");
// this regular expression used for search city code
var matcher = new RegExp("([(*)])" + re, "i");
How do I combine these two regular expressions into a single regex that works as described?
I suggest this:
var myregexp = /^([^,]+),[^(]*\(([^()]+)\)/;
var match = myregexp.exec(subject);
if (match != null) {
city = match[1];
code = match[2];
}
Explanation:
^ # Start of string
( # Match and capture (group number 1):
[^,]+ # One or more characters except comma (alternatively insert city name)
) # End of group 1
, # Match a comma
[^(]* # Match any number of characters except an opening parenthesis
\( # Match an opening parenthesis
( # Match and capture (group number 2):
[^()]+ # One or more characters except parentheses (alt. insert city code)
) # End of group 2
\) # Match a closing parenthesis
This assumes that no city name will ever contain a comma (otherwise this regex would only capture the part before the comma), so you'd need to check your data if that's ever possible. I can't think of an example, but that's not saying anything :)
$("#leavingCity").autocomplete({
source: function(req, responseFn) {
var re = $.ui.autocomplete.escapeRegex(req.term);
var matcher = new RegExp("/^([^,]+),[^(]*\(([^()]+)\)/", "g");
var a = $.grep(cityList, function(item,index) { return matcher.test(item); });
responseFn(a);
} });
Try this, regualr expression by Tim Pietzcker
This is the most elegant way I can do it:
var cityList = ["Anaa, French Polynesia (AAA)","Arrabury, Australia (AAB)","Al Arish, Egypt (AAC)","Ad-Dabbah, Sudan (AAD)","Annaba, Algeria (AAE)","Apalachicola, United States (AAF)","Arapoti, Brazil (AAG)","Aachen, Germany (AAH)","Arraias, Brazil (AAI)","Awaradam, Suriname (AAJ)","Aranuka, Kiribati (AAK)","Aalborg, Denmark (AAL)"];
var regex = /([a-z].+?),.+?\(([A-Z]{3,3})\)/gi, match, newList = [];
while (match = regex.exec(cityList)) {
newList.push(match[1]+" - "+match[2]);
}
alert(newList[7]);
// prints Aachen - AAH
If you don't understand how to use parentheses in your regex, I suggest you check out the site I learned from: http://www.regular-expressions.info/
Here I suggest a completly different approach (ECMA-262 standard).
As using the regex requires a linear search anyway, if you can pre-process the data, you can set up an array of city objects:
function City(name, country, code){
this.cityName = name;
this.cityCountry = country;
this.cityCode = code;
}
var cities = [];
cities.push(new City('Anaa', 'French Polynesia', 'AAA'));
// ... push the other cities
And a search function:
function GetCity(cityToSearch, cities){
var res = null;
for(i=0;i<cities.length;i++){
if(cities[i].city = cityToSearch
res = cities[i];
}
return res;
}
At run time:
var codeFound = '';
var cityFound = GetCity('Arraias');
if(cityFound != null)
codeFound = cityFound.cityCode;
Remark
In both case, if you are going to fill the cities array with all city of the world, the city name is not a key! For instance there are half a dozen of 'Springfield' in USA. In that case a better approach is to use a two-fields key.
I think you want to accomplish this in a few simple steps:
Split each string in your array before and after the first parenthesis
Apply your first regex to the first part of the string. Store the result as a boolean variable, perhaps named matchOne
Apply your second regex to the second part of the string (don't forget to remove the closing parenthesis). Store the result as a boolean variable, perhaps named matchTwo.
Test if either of the two mathes succeeded: return ( matchOne || matchTwo );
Use indexOf
Its more efficient and explicit of expectation. regex is unnecessary.
const isMatchX = cityList.indexOf('AAB');
const isMatchY = cityList.indexOf('Awar');
Alternatively you could so something like this but its way overkill when you can use indexOf:
const search = (cityList, re) => {
const strRegPart1 = "¬[^¬]*" + re + "[^¬]*";
const strRegPart2 = "¬[^¬]*\\([^\\)]*" + re + "[^\\)]*\\)($|¬)";
const regSearch = RegExp("(" + strRegPart1 + "|" + strRegPart2 + ")", "gi");
const strCityListMarked = '¬' + cityList.join('¬');
const arrMatch = strCityListMarked.match(regSearch);
return arrMatch && arrMatch[1].substr(1);
}

jquery ui autocomplete: how to match "whitespace" separated query with accurate "comma" separated record

I have preloaded xml records on the client side, on which jquery ui autocomplete is applied.
Say there is a record like this one: "car,bike,plane".
Currently (while using jquery ui autocomplete) if a user types : "car,bike" --> it will find the record mentioned above.
However if the user types: "car bike" (which means instead of comma, a whitespace ) --> no results will be displayed.
How to match "car bike" with the given "comma" separated record?
I think that you can split the two arrays and loop one of them searching the index of the looped items at the other array.
<script type="text/javascript">
var str="car,bike,plane";
var strToSearch= "car bike";
var splitted = str.split(",");
var strToSearchSplitted = strToSearch.split(' ');
var found = false;
for (var i in splitted){
if (strToSearchSplitted.indexOf(splitted[i]) != -1){
found = true;
break;
}
}
<script>
For the peeps who might be dealing with the same situation, here is the solution i came up with, a brief explanation first:
First, i split the search term (query) on white space
I then dynamically build a regex definition by concatenating the splitted words with some more regex attributes. For example: say user searches for "Car Bike" --> after splitting i dynamically build the following string:
[str = "Car"+(any character(s))+"Bike"+(any character(s))] , which is then fed to javascript RegExp.
To make it more clear, here is my Code:(hopefully it might help you)
$( "#field" ).autocomplete({
source:function(request, response) {
var query_array = request.term.toUpperCase().split(/\s+/g);//split on white space
if(query_array.length >1)// if two ore more words, separated by whitespace
{
if(!/^\s*$/.test(query_array[query_array.length-1]))//if last word is not only whitespace
{
var new_query = '';
for (var i=0; i<query_array.length; ++i )
{
new_query+='(\\b' +query_array[i]+'\\b)';//word boundaries
if(i<query_array.length-1)
{
new_query+="([\\s\\S\\.]*)";// match ALL characters including newline , * is for many
}
}
var matcher = new RegExp('^'+new_query, "g");//must Start with query term, apply regex global
var a = $.grep( autocomplete_data_array, function(item,index){
return matcher.test(item.value);
});
if(a.length>0)
{
response(a);
}
}
}

Categories

Resources