Match all phrases and push element to array - javascript

I am trying to match all typed phrases(characters) with main array. I have a big array, when user types something i want to search my array and show only matching entries, for example: if user types puzzle then from my array 'my puzzle starts','enter your puzzle' these both should match.
I have created Fiddle here, here is my html code
Search: <input autocomplete="off" type="text" id="search" /><br />
Results: <input type="text" id="results_count" /><br />
<textarea id="results_text" style="width:300px; height:500px"></textarea>
My js code
var string =['1234sud','man in puzzle','rocking roll','1232 -powernap','all_broswers'];
$('#search').keyup(function() {
var search = $(this).val();
var resultsText = $('#results_text');
var resultsCount = $('#results_count');
if (!search) {
resultsText.val('');
resultsCount.val('0');
return;
}
var j = 0, results = [],result;
for (var i=0;i<string.length;i++){
var rx = new RegExp('"([*'+search+'*])"','gi');
if (rx.exec(string[i])) {
results.push(string[i]);
j += 1;
if (j >=100)
break;
}
}
//results=string;
resultsText.val(results);
console.log(results)
resultsCount.val(j);
});
I am not able to write that regular expression which matches types phrases (characters) with array and pushes matching element to result array. Any help will be appreciated thanks in advance.

var string = ['1234sud', 'man in puzzle', 'rocking roll', '1232 -powernap', 'all_broswers'];
$('#search').keyup(function() {
var search = $(this).val();
var resultsText = $('#results_text');
var resultsCount = $('#results_count');
if (!search) {
resultsText.val('');
resultsCount.val('0');
return;
}
var j = 0,
results = [],
result;
for (var i = 0; i < string.length; i++) {
if (string[i].indexOf(search) > -1) {
results.push(string[i]);
j += 1;
if (j >= 100)
break;
}
}
//results=string;
resultsText.val(results);
console.log(results)
resultsCount.val(j);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
Search: <input autocomplete="off" type="text" id="search" /><br /> Results: <input type="text" id="results_count" /><br />
<textarea id="results_text" style="width:300px; height:500px"></textarea>
Use String.prototype.indexOf() function, it's exactly what you want to accomplish.

Normalize the case and use Array#filter()
$('#search').keyup(function() {
var search = $(this).val().toLowerCase(),
// add validation that search isn't empty, return if it is
// create results array
results = string.filter(function(s){
return s.toLowerCase().indexOf(search)>-1;
})
// do something with results
})

Your simplest solution: don't use regex.
For this particular problem, you're best off just looking for the index of the substring, using indexOf.
const words = ['hello', 'jello', 'cake', 'pie'];
const term = 'ello';
const result = words.filter(word => word.indexOf(term) > -1);
console.log(result);
You don't specify, but if you want word only matches (as oppose to partial word matches like I have above), you could pad it with a space.
If you insist on using regex, then all you need is the word itself:
const words = ['hello', 'jello', 'cake', 'pie'];
const term = 'ello';
const result = words.filter(word => (new RegExp(term, 'gi')).test(word));
console.log(result);
You don't need any fancy symbols or groupings or anything.
A regex of /ello/ just means "find this word anywhere in the string". Since there isn't a ^ or $, it isn't limited by start or end, so no need for wildcards.
Also, use test() instead of exec(), since test() resolves to a boolean, it's more accurate for an if statement.
Again, if you want whole words, you could just wrap it in spaces with the start and end having /[^\s]word[\s$]/ which would means "either a space or the start of the phrase" and "either a space or the end of the phrase".

Maybe you want to use a filter() for that task.
var words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
// const result = words.filter(word => word.indexOf(words.search) >= 0);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Hope this helps.

Use String.match if you need exact match:
if (search.match(string[i])) {
results.push(string[i]);
j += 1;
}
Or dynamically created regex for case of not exact match:
var rxs = '.*' + search + '.*';
var rx = new RegExp(rxs,'gi');

const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
let checkWord = "elite"
words.forEach(x=>{
checkWord.match(new RegExp(x)
})

Related

JS String how to extract an array of substring that matches a condition?

I have a string like this:
"|hello| + |world| / |again|"
And I need to get the substrings inside the | and return an array like this:
["hello", "world", "again"]
What is the best approach to accomplish this?
You can use a regex that searches for groups consisting only of letters (([A-Za-z])) between pipes (/|) where the pipes are to omitted from the actual match (wrapped with (?:))-- then use .matchAll to get all the matches and .map the result to get the captured groups only (non-captures omitted) -- see below:
const str = "|hello| + |world| / |again|";
const re = /(?:\|)([A-Za-z]+)(?:\|)/g;
const results = [...str.matchAll(re)].map((entry) => entry[1]);
console.log(results);
This will work to match only those words that are between pipes. If you have other words in your string that are not wrapped between pipes, they will be ignored. Like the following snippet:
const str = "|hello| + |world| / |again| how are |you| doing?";
const re = /(?:\|)([A-Za-z]+)(?:\|)/g;
const results = [...str.matchAll(re)].map((entry) => entry[1]);
console.log(results);
array = [];
let name = "";
let input = "|hello| + |world| / |again|";
let index = 0;
let isStart = false;
while (index < input.length) {
if (input[index] == '|') {
isStart = !isStart;
if (!isStart) {
array.push(name);
name = "";
}
} else {
if (isStart) {
name = name + input[index];
}
}
index++;
}
console.log(array);
If that string doesn't change format use a regular expression to match against multiple lower-case letters.
const str = '|hello| + |world| / |again|';
const regex = /[a-z]+/g;
console.log(str.match(regex));

Iterate through arrays inside a .replace()

I need to replace all letters from a string to other signs (stored in an array).
for (var i = 0; i < letters.length; i++) {
output.value = input.value.replace(letters[i], signs[i]); //this should be global
}
var input = document.getElementById('input');
var output = document.getElementById('output');
var letters = ['a', 'b', 'c'];
var signs = ['.', ':', '-'];
function run() {
for (var i = 0; i < letters.length; i++) {
output.value = input.value.replace(letters[i], signs[i]); //this should be global
}
}
<input type="text" id="input" onkeyup="run();">
<input type="text" id="output">
If the input says "abc" the output should say ".:-"
The problem is the new updated value after the replacing step, what you need to do is store the new value and after replacing the whole set of chars, then set the new value to output.value.
An important detail here is that you need to replace all chars which match with a specific letter, to accomplish that you can build a Regexp and use the flag global g.
new RegExp(letters[i], 'g');
^
|
+---- This is the flag!
Another thing I recommend is to embrace the function addEventListener to bind an event to elements.
var input = document.getElementById('input');
var output = document.getElementById('output');
var letters = ['a', 'b', 'c'];
var signs = ['.', ':', '-'];
function run() {
var currentInput = this.value;
for (var i = 0; i < letters.length; i++) {
var rg = new RegExp(letters[i], 'g');
currentInput = currentInput.replace(rg, signs[i]);
}
output.value = currentInput;
}
input.addEventListener('input', run);
<input type="text" id="input">
<input type="text" id="output">
I would turn you letters and signs into a lookup table like:
{a: ',', b:':' // etc..}
so you don't need to search through the letters with each keyup. You can do this once at the beginning, or just use the format to begin with. The you can just map() it to a new value
var input = document.getElementById('input');
var output = document.getElementById('output');
var letters = ['a', 'b', 'c'];
var signs = ['.', ':', '-'];
let signMap = letters.reduce((a, c, i) => {
a[c] = signs[i]
return a
}, {})
function run() {
output.value = [...input.value].map((i) => signMap[i]).join('')
}
<input type="text" id="input" onkeyup="run();">
<input type="text" id="output">
Another alternative if your letters are always going to be in order is to use the character codes for the lookup in signs. You can also use replace with a generic regex and pass the letter to the function. Then you can avoid the loop altogether. This will ignore input not in the signs, but you could easily just include the original letter if it's not in the signs array.
var input = document.getElementById('input');
var output = document.getElementById('output');
var signs = ['.', ':', '-'];
let offset = 'a'.charCodeAt(0)
function run() {
output.value = input.value.replace(/./g, s => signs[s.charCodeAt(0) - offset] || '' )
}
<input type="text" id="input" onkeyup="run();">
<input type="text" id="output">
My opinion:
I prefer to create one Object to store all mappings, like what signMap does.
create one regex like (a|b|c|ef) (so you don't need to loop each character for input string, then even it can support multiple letters). PS: assuming the elements in Array=letters are not special letters, if yes, you need to adjust Regex Expression for your real case.
then uses String.replace(regex, (replacement)=>{return 'what you need'})
like below simple demo:
var input = document.getElementById('input');
var output = document.getElementById('output');
var letters = ['a','b','c', 'ef'];
var signs = ['.',':','-', '#'];
let regex = new RegExp('('+letters.join('|') + ')', 'g') //assume the elements in Array=letters doesn't have special characters.
let signMap = letters.reduce((pre, cur, curIndex) => {
pre[cur] = signs[curIndex]
return pre
}, {})
function run() {
output.value = input.value.replace(regex, (item)=> {
return signMap[item]
});
}
<input type="text" id="input" onkeyup="run();">
<input type="text" id="output">

How to get biggest number in textarea?

I have a textarea like this:
<textarea>
this is a test [1] also this [2] is a test
and again [3] this is a test
</textarea>
Now I need to get the biggest number which is in []. In this case I need to get 3. How can I do that?
You could do:
var result = Math.max.apply(Math, textarea.value.match(/\d+/g).map(Number));
Breaking it up:
textarea.value.match(/\d+/g)
Gets you an array of numbers as strings.
.map(Number)
Maps each entry of the array from a string to a number.
Math.max.apply
Calls Math.max with this as Math and as parameters the mapped array.
Edit: I didn't realize what you needed had to be in between brackets. You'll need to use a capture group for that and it's a little bit more complicated now.
var reg = /\[(\d+)\]/g, numberStrings = [ ], match;
while((match = reg.exec(textarea.value)) !== null){
numberStrings.push(match[1]);
}
var result = Math.max.apply(Math, numberStrings.map(Number));
It's a little bit more tricky to get the array of strings with the numbers.
Another alternative, without using a capture group:
var numbersInBrackets = textarea.value.match(/\[\d+\]/g);
var numbers = numbersInBrackets.map(function(x) {
return Number(x.substring(1, x.length - 1));
});
var result = Math.max.apply(Math, numbers);
Same idea as MinusFour's solution. Uses jQuery but could easily be done without.
var content = $('textarea').val();
var contentArr = content.split(' ');
var nums = [];
for (var i = 0; i < contentArr.length; i++) {
var txt = contentArr[i];
if (txt.match(/[\d]/)) {
nums.push(Number(txt.slice(1,-1)));
}
}
// Max number is Math.max.apply(null, nums)
Full working JSFiddle.
You need to perform 2 actions:
Get all the [(numbers)] with \[(\d+)] regex
Get the max value from the resulting array (see this post)
Array.max = function( array ){
return Math.max.apply( Math, array );
};
var re = /\[(\d+)]/g;
var str = 'this is a test [1] also this [2] is a test\nand again [3] this is a test';
var numbers = []
while ((m = re.exec(str)) !== null) {
numbers.push(Number(m[1]));
}
document.write(Array.max(numbers));
Use this function to find the biggest [number] in any string :
var biggestNumber = function(str) {
var pattern = /\[([0-9]+)\]/g, match, biggest = 0;
while ((match = pattern.exec(str)) !== null) {
if (match.index === pattern.lastIndex) {
pattern.lastIndex++;
}
match[1] = parseInt(match[1]);
if(biggest < match[1]) {
biggest = match[1];
}
}
return biggest;
}
DEMO
The following demo calculates the biggest number in your textarea every time you click the button.
It allows you to play around with the textarea and re-test the function with a different text.
var biggestNumber = function(str) {
var pattern = /\[([0-9]+)\]/g, match, biggest = 0;
while ((match = pattern.exec(str)) !== null) {
if (match.index === pattern.lastIndex) {
pattern.lastIndex++;
}
match[1] = parseInt(match[1]);
if(biggest < match[1]) {
biggest = match[1];
}
}
return biggest;
}
document.getElementById("myButton").addEventListener("click", function() {
alert(biggestNumber(document.getElementById("myTextArea").value));
});
<div>
<textarea rows="4" cols="50" id="myTextArea">
this is a test [1] also this [2] is a test
and again [3] this is a test
</textarea>
</div>
<div>
<button id="myButton">Try me</button>
</div>
See also this Fiddle!

How to match overlapping keywords with regex

This example finds only sam. How to make it find both sam and samwise?
var regex = /sam|samwise|merry|pippin/g;
var string = 'samwise gamgee';
var match = string.match(regex);
console.log(match);
Note: this is simple example, but my real regexes are created by joining 500 keywords at time, so it's too cumbersome to search all overlapping and make a special case for them with something like /sam(wise)/. The other obvious solution I can think of, is to just iterate though all keywords individually, but I think it must be a fast and elegant, single-regex solution.
You can use lookahead regex with capturing group for this overlapping match:
var regex = /(?=(sam))(?=(samwise))/;
var string = 'samwise';
var match = string.match( regex ).filter(Boolean);
//=> ["sam", "samwise"]
It is important to not to use g (global) flag in the regex.
filter(Boolean) is used to remove first empty result from matched array.
Why not just map indexOf() on array substr:
var string = 'samwise gamgee';
var substr = ['sam', 'samwise', 'merry', 'pippin'];
var matches = substr.map(function(m) {
return (string.indexOf(m) < 0 ? false : m);
}).filter(Boolean);
See fiddle console.log(matches);
Array [ "sam", "samwise" ]
Probably of better performance than using regex. But if you need the regex functionality e.g. for caseless matching, word boundaries, returned matches... use with exec method:
var matches = substr.map(function(v) {
var re = new RegExp("\\b" + v, "i"); var m = re.exec(string);
return (m !== null ? m[0] : false);
}).filter(Boolean);
This one with i-flag (ignore case) returns each first match with initial \b word boundary.
I can't think of a simple and elegant solution, but I've got something that uses a single regex:
function quotemeta(s) {
return s.replace(/\W/g, '\\$&');
}
let keywords = ['samwise', 'sam'];
let subsumed_by = {};
keywords.sort();
for (let i = keywords.length; i--; ) {
let k = keywords[i];
for (let j = i - 1; j >= 0 && k.startsWith(keywords[j]); j--) {
(subsumed_by[k] = subsumed_by[k] || []).push(keywords[j]);
}
}
keywords.sort(function (a, b) b.length - a.length);
let re = new RegExp('(?=(' + keywords.map(quotemeta).join('|') + '))[\\s\\S]', 'g');
let string = 'samwise samgee';
let result = [];
let m;
while (m = re.exec(string)) {
result.push(m[1]);
result.push.apply(result, subsumed_by[m[1]] || []);
}
console.log(result);
How about:
var re = /((sam)(?:wise)?)/;
var m = 'samwise'.match(re); // gives ["samwise", "samwise", "sam"]
var m = 'sam'.match(re); // gives ["sam", "sam", "sam"]
You can use Unique values in an array to remove dupplicates.
If you don't want to create special cases, and if order doesn't matter, why not first match only full names with:
\b(sam|samwise|merry|pippin)\b
and then, filter if some of these doesn't contain shorter one? for example with:
(sam|samwise|merry|pippin)(?=\w+\b)
It is not one elegant regex, but I suppose it is simpler than iterating through all matches.

Test a Textarea for All Keywords in an Array

I found a variation on this code elsewhere in StackOverflow. It takes all words from a textarea and converts them into a regular expression. It then tests an array to see if all the words in the regex are contained in the array:
<textarea id="inputtext" type="text"></textarea>
<input id="searchbutton" type="button" value="Click me" />
var links = new Array("taxi","Alpha","runway");
$("#searchbutton").click(function () {
var query = $("#inputtext").val();
var querywords = query.split(',');
for (var i = 0; i < querywords.length; i++) {
var regex = new RegExp('(?=.*\\b' + querywords[i].split(' ').join('\\b)(?=.*\\b') + '\\b)', 'i', 'g');
for (var j = 0; j < links.length; j++) {
if (regex.test(links[j])) {
console.log("Correct");
}
}
}
});
How can I reverse the process so the program returns "true" if the textarea words includes all of the keywords within the array? For example, if the textarea had the sentence "Taxi to the runway via taxiway alpha," and the array named "links" contained the keywords "taxi" "alpha" and "runway", the program would return "true".
That script you have seems to check if any of the words appears somewhere in the array. What you want is the every Array method:
var text = "Taxi to the runway via taxiway alpha",
links = ["taxi", "alpha", "runway"];
console.log( links.every(function(word) {
return new RegExp("\\b"+word+"\\b", "i").test(text);
}) ); // true
The methods provided by other answers are simple, but they could be more efficient.
It's almost always better to use an object as a map to speed up lookups instead of having to search the entiry array everytime.
var words = ['word1', 'word2'],
wordsMap = 'text area content, word1 and word2'.split(/\W+/).reduce(function (obj, word) {
obj[word] = true;
return obj;
}, {}),
areWordsAllContained = words.every(function (word) {
return wordsMap[word.toLowerCase()];
});
console.log(areWordsAllContained); //true
EDIT: I've changed the splitting regex from \s+ to \W+ to make sure that it splits on every non-word characters.
A non-regex way would be:
var arr = ['word1', 'word2'], haystack = textArea.value.toLowerCase().split(/\s+/);
var result = true, i = 0;
for(i=0; i<arr.length; i++) {
if(haystack.indexOf(arr[i].toLowerCase()) === -1) {
result = false;
break;
}
}

Categories

Resources