Need Regular Expression to fetch string data from array with exact match - javascript

I want to match and store string in an array using javascript and need regular expression for this. Other solution also appreciated. separated by '_' if only both matches in string those string from array should be retured only, no other match should be accepted. Array and search string both are dynamic. Below is just an example but solution shoud match for any dynamic data.
Example problem given below.
let arr1 = ['ef','cd','ab','cdab','efab','cdef','ab/cd/ef','cd/ef,ab','cd/ab','ab/ef']
test scenarios:
let search = 'ef_ab';
expected output would be ['efab','ab/ef']
let search = 'ab_cd_ef';
expected output would be ['ab/cd/ef','cd/ef,ab']
let search = 'cd';
expected output would be ['cd']
Any help in javascript for problem is appreciated.
I have tried below regex and looping.
Here word1 for given example could be ab or cd or ef and same could be for word2 , word3
let arr1 = ['ef', 'cd', 'ab', 'cdab', 'efab', 'cdef', 'ab/cd/ef', 'cd/ef,ab', 'cd/ab', 'ab/ef']
let regex = /(?=.*word1)(?=.*word2)(?=.*word3)/;
let arr2 = [];
for (i = 0; i < arr1.length; i++) {
if (regex.test(arr1[i]))
arr2.push(arr1[i]);
}
console.log(arr2)

You may use the input to form a series of regex patterns, each of which must match against the input string. An input which matches all regex patterns is a valid match.
var arr1 = ['ef','cd','ab','cdab','efab','cdef','ab/cd/ef','cd/ef,ab','cd/ab','ab/ef'];
var search = 'ab_cd_ef';
var parts = search.split("_");
for (var i=0; i < arr1.length; ++i) {
var counter = 0;
for (var p=0; p < parts.length; ++p) {
var r = new RegExp("\\b" + parts[p] + "\\b");
if (r.test(arr1[i])) {
++counter;
}
}
if (counter == parts.length) {
console.log(arr1[i] + " => MATCH");
}
}

We need to get some permutations going:
// Heap's algorithm https://stackoverflow.com/a/66122464/295783
const excluding = (i) => (xs) => [... xs.slice (0, i), ... xs.slice (i + 1)];
const permutations = (xs) => xs.length == 0 ? [[]] : xs.flatMap ((x, i) => permutations (excluding (i) (xs)).map (p => (x +' '+ p).trim()));
const findSequence = (arr,str) => {
const parts = str.split("_");
const re = new RegExp(`^${permutations(parts) // ^ from start
.map(part => `${part.replace(/ /g,".?")}`) // with a character or not in between
.join('|')}$`); // to end $ of each string
console.log(re); // just to show the resulting regexp
return arr.filter(item => item.match(re));
}
let arr1 = ['ef', 'cd', 'ab', 'cdab', 'efab', 'cdef', 'ab/cd/ef', 'cd/ef,ab', 'cd/ab', 'ab/ef']
console.log(findSequence(arr1,'ef_ab')) // ['efab','ab/ef']
console.log(findSequence(arr1,'ab_cd_ef')) // ['ab/cd/ef','cd/ef,ab']
console.log(findSequence(arr1,'cd')) // ['cd']

Related

How to extract specific words from a string with some patterns?

I am trying to extract some strings from a word with some pattern like -
"38384-1-page1-2222", "1-22-page33-02", "99-222-frontpage-111"
how will I extract all word between - separately, means first word before - and then second word between - and - and so on...
string = "38384-1-page1-2222";
string.substr(0, string.indexof("-")); //return 38384
But how will I extract 1, page1 and 2222 all the words separately?
The javascript function str.split(separator) split the string by the given separator and it returns an array of all the splited string. REF Here
Here is an example following your question :
var string = "38384-1-page1-2222";
var separator = "-";
var separated = string.split(separator);
var firstString = separated[0]; // will be '38384'
var secondString = separated[1]; // will be '1'
var thirdString = separated[2]; // will be 'page1'
/* And So on ... */
Hope this can help
Use String.prototype.split() to get your string into array
var words = ["38384-1-page1-2222", "1-22-page33-02", "99-222-frontpage-111"];
var resultArray = [];
for (let i = 0; i < words.length;i++) {
let temp = words[i];
resultArray = pushArray(temp.split("-"), resultArray)
}
console.log(resultArray)
function pushArray (inputArray, output) {
for (let i = 0; i < inputArray.length;i++) {
output.push(inputArray[i]);
}
return output;
}
Or simply use Array.prototype.reduce()
var words = ["38384-1-page1-2222", "1-22-page33-02", "99-222-frontpage-111"];
var result = words.reduce((previousValue, currentValue) => previousValue.concat(currentValue.split("-")), [])
console.log(result)
You can use regex /[^-]+/g
const words = ["38384-1-page1-2222", "1-22-page33-02", "99-222-frontpage-111"];
console.log(words.map(v=>v.match(/[^-]+/g)).flat())

Splitting string into array based on first and last

I have this array :-
var a = [' DL1,C1,C5,C6','M4,DL3-7,B1-5']
And I want to split them like
[DL1,C1,C5,C6,M4,DL3,DL4,DL5,DL6,DL7,B1,B2,B3,B4,B5]
So that DL3-7 or DL3-DL7 this Split like this DL3,DL4,DL5,DL6,DL7
Reason why I am doing this, is because I want to block duplicate entry like DL3 should not come anywhere else, I am trying for loops to do this, just want to know if there is any simpler way to do it, and check for duplicacy afterwards.
Thanks
You have to break down your problems into three parts:
getting comma delimited values into different array items
resolving "DL3-7" to "DL3", "DL4"...
removing duplicates
Once you break down the problem, it is much easier to handle them one by one. The code is pretty readable, let me know if there is anything difficult to understand what's going on.
const a = ['DL1,C1,C5,C6', 'M4,DL3-7,B1-5']
//this will split all comma delimited values
const commaDelimit = a.map(item => item.split(',')).flat();
console.log("Separate values by comma: ")
console.log(commaDelimit);
//this will turn the ranges into individual items
//this does not account for if the number is bigger than 9.
//you can try doing this part yourself if you need to, should be a good learning exercise.
const resolveRange = commaDelimit.map(item => {
if (item.includes('-')) {
const pos = item.indexOf('-');
const beginning = Number(item.charAt(pos - 1));
const end = Number(item.charAt(pos + 1)) + 1;
const toReturn = [];
const prependString = item.substring(0, pos - 1);
for (let i = beginning; i < end; i++) {
toReturn.push(`${prependString}${i}`)
}
return toReturn;
}
return item;
}).flat();
console.log("Change 'DL3-7' to DL3, DL4 and so on: ")
console.log(resolveRange);
//this will get rid of duplicates
const uniques = [...new Set(resolveRange)];
console.log("Remove duplicates: ")
console.log(uniques);
Create an Array with that length, iterate and transform,
I've just wrote the most challenged part:
function splitRange(range) {
let a = range.split('-');
if (a.length < 2) return [range];
const baseString = (a[0].match(/[a-z A-Z]/g))?.join('');
const baseNumber = +((a[0].match(/\d+/))?.shift());
return Array.from({length: +a.pop().match(/\d+/) - baseNumber + 1}).map((_,i)=>`${baseString}${i+baseNumber}`);
}
const s='DL1,C1,C5,C6,M4,DL3-7,B1-5';
console.log(
s.split(',').map(item=>splitRange(item)).flat()
);
Basically, #cSharp has explained the concept of data transformation to the desired output.
Split by comma.
Work with regex to transform the range value and append it to the array. Regex pattern & test data
Distinct the array value.
var a = [' DL1,C1,C5,C6','M4,DL3-7,B1-5'];
var formatteds = a.reduce((previous, current) => {
var splits = current.trim().split(',');
var rangedSplits = splits.reduce((prev, cur) => {
var pattern = new RegExp(/([A-Z]*)(\d)-[A-Z]*(\d)/);
var match = pattern.exec(cur);
if (match) {
// Pattern 1: ['DL3-7', 'DL', '3', '7']
// Pattern 2: ['DL3-DL7', 'DL', '3', '7']
var startIndex = parseInt(match[2].toString());
var endIndex = parseInt(match[3].toString());
var arr = [];
for (let i = startIndex; i <= endIndex; i++) {
arr.push(match[1].toString() + i);
}
prev = prev.concat(arr);
} else {
prev = prev.concat([cur]);
}
return prev;
}, []);
previous = previous.concat(rangedSplits);
return previous;
}, []);
var result = formatteds.filter((x, i, array) => array.indexOf(x) === i);
console.log(result);

Find indices within a string where any combination of an array of words is found

Sample data:
String: "barfoofoobarthefoobarman"
Array of words: ["bar", "foo", "the"]
Output:
[6, 9, 12]
I was asked this question during an interview. Due to time constraint, I tried to find all the possible words that could be made out of the array of words (i. e. "barfoothe"), but was told that would not scale for large arrays. Was suggested to use a map data structure, but I think my solution doesn't scale either, and it's brute forced.
Here's the solution.
var solution = function(string, words) {
let output = [];
let wordsMap = new Map();
let wordsNumber = words.length;
let wordLength = words[0].length;
words.forEach((word) => {
if (!wordsMap.has(word))
wordsMap.set(word, 1);
else
wordsMap.set(word, wordsMap.get(word) + 1);
});
for (let i = 0; i <= string.length-(wordsNumber*wordLength); i+=wordLength) {
let tempMap = new Map(wordsMap);
let check = true;
let tempString = string.substring(i, i + wordsNumber*wordLength);
for (let j = 0; j <= tempString.length - wordLength; j += wordLength) {
let tempString2 = tempString.substring(j, j + wordLength);
if (tempMap.has(tempString2))
tempMap.set(tempString2, tempMap.get(tempString2) - 1);
}
for (let val of tempMap.values()){
if (val !== 0){
check = false
break;
}
}
if (check)
output.push(i)
}
console.log(output);
}
solution("barfoothefoobarman", ["foo", "bar"]);
Any suggestion for a smarter solution?
You could create a dynamic regular expression.
const words = ['foo', 'bar']
const rx = new RegExp(words.join('|'), 'g')
// todo escape special characters
Then search away.
const counts = words.map(it=>0) // [0,0]
// todo use map or object to track counts instead of array
while (m = rx.exec(inputString)) {
const index = words.indexOf(m[0])
counts[index]++
}
Thank you for your question. I think the question in the interview was less about the right solution and more about the right approach.
The trickiest part is actually just finding the word combinations. There are several approaches here. For me it's a clear case for recursion.
So my approach would be:
find all word combinations, except combinations with itself (for example: foofoo or barbar).
iterate through the word combinations and ask whether they are contained in the string.
extra: Sort SolutionArray
Done!
Note: I use indexOf() for point 2 but I think a regex match would make it even better because you find all possibilities of a word in a string and not just the first one like with indexOf. Would make sense for longer strings.
const arr = ["foo", "bar"];
const str = "barfoothefoobarman"
let res = [];
const combinations = (len, val, existing) => {
if (len == 0) {
res.push(val);
return;
}
for(let i=0; i<arr.length; i++) {
if(! existing[i]) {
existing[i] = true;
combinations(len-1, val + arr[i], existing);
existing[i] = false;
}
}
}
const buildCombinations = (arr = []) => {
for(let i = 0; i < arr.length; i++) {
combinations(arr.length - i, "", []);
}
};
buildCombinations(arr);
// exclude the base wordes from result array
newRes = res.filter((e) => {
if (! arr.includes(e)) {
return e;
}
})
console.log('all word combinations:', newRes);
// get the string position
const _positions = [];
newRes.forEach((w) => {
let res = str.indexOf(w);
if (res != -1 && ! _positions.includes(res)) {
_positions.push(res);
}
})
// sort array and use Float64Array to speed up
const positions = new Float64Array(_positions)
console.log('positions', positions.sort())

Take a string , evaluate it and find if there is a number and repeat part of string that number of times?

I was writing code and came into this problem,
You have a specific string which is in this form:
d ae2 n s
now we have to decode this in a specific way,
Split it into different parts by spaces to make an array like ["d","ae2","n","s"]
Evaluate each element of the array and find out if there is a number in it.
If there is a number then repeat the string the number of times.
Add it into the array and continue.
So the output array should be
["d","ae","ae","n","s"]
I have already tried a lot but got nothing
I have used this code earlier but it ends on the second string:
var str = "d ae2 n s"
var res = str.split(" ");
alert(res.length);
for(var x = 0; x < res.length; x++ ){
var std = res[x];
var fun = checkNum(std);
if(fun === true){
var numbers = str.match(/\d+/g).map(Number);
var index = res.indexOf(std);
var result = std.replace(/[0-9]/g, '');
var res2 = result.repeat(numbers);
res[index] = res2;
}
else{
continue;
}
for(var i = 0; i < res.length; i++ ){
console.log(res[x]);
}
}
function checkNum(t){
return /\d/.test(t);
}
// I am a terible coder :/
expected input : d ae2 n s
expected output : ["d","ae","ae","n","s"]
Using fill() and flatMap() methods and
regex replace
/[^0-9]/ - all non numerical chars
/[0-9]/ - all numerical chars
var str = 'd ae2 n s'
var res = str
.split(' ')
.flatMap(i =>
Array(+i.replace(/[^0-9]/g, '') || 1)
.fill(i.replace(/[0-9]/g, ''))
)
console.log(res)
You can simply loop over your array and populate an other array that will hold your result after checking for a number :
const results = [];
"d ae2 n s".split(' ').forEach(token => {
const match = token.match(/\d+/);
if (match) {
const newStr = token.split(/\d/)[0];
for (let i = 0; i < match[0]; i++) {
results.push(newStr);
}
} else {
results.push(token)
}
})
console.log(results);
You can check Seblor's answer for optimized logic. I have modified your code so that it will be easy for you to understand where you went wrong while doing this. I have added comments to your code where I have changed things:
var str = "d ae2 n s"
var res = str.split(" ");
// create a variable to store the output.
var output = [];
for(var x = 0; x < res.length; x++ ){
var std = res[x];
var fun = checkNum(std);
if(fun === true){
// map returns an array, so take the first element, it will be your number.
var numbers = str.match(/\d+/g).map(Number)[0];
var index = res.indexOf(std);
var result = std.replace(/[0-9]/g, '');
// instead of doing the repeat and updating the current index,
// push the result, i.e. the current string to be repeated "numbers" times into
// the output array.
for (var i = 0; i < numbers; i++) {
output.push(result)
}
}
else{
// if does not contain any number, push the current item to ouput
output.push (std);
continue;
}
}
function checkNum(t){
return /\d/.test(t);
}
console.log(output);
You can do:
const str1 = 'd ae2 n s';
const str2 = 'e d aefg4 m n s';
const regex = /\d+/;
const getResult = input => input.split(' ').reduce((a, c) => {
const n = c.match(regex);
return n
? [...a.concat(c.replace(n, ' ').repeat(n).trim().split(' '))]
: [...a, c];
}, []);
console.log(getResult(str1));
console.log(getResult(str2));
you can use the Array prototype reduce and filter
const input = 'd ae2 n s';
const output = input.split(' ').reduce((memory, current) => {
const numberIndex = current.split('').findIndex(c => !isNaN(c));
const newCurrent = current.split('').filter((_, index) => index !== numberIndex).join('');
if(numberIndex !== -1) {
for(let i = 0; i < parseInt(current[numberIndex]); i++) {
memory.push(newCurrent);
}
} else {
memory.push(current);
}
return memory;
}, []);
console.log(output);
Hope this helped
You can try with following:
let str = "d ae2 n s"
let split = str.split(" ")
let rx = new RegExp("[0-9]")
let res = [];
split.forEach(s => {
if(rx.exec(s) !== null) {
let rxResult = rx.exec(s)
let count = rxResult[0];
let matchIdx = rxResult[1];
for(let i = 0; i < count; i++) {
res.push(s.replace(count, ""))
}
} else {
res.push(s);
}
})

Join Regex Expression Array

I have an array of words e.g. apple, banana, horse which I want to have in a later function as split points.
I found this how to concat regex expressions, but it is for a fixed number of expressions:
How can I concatenate regex literals in JavaScript?
Question:
How to join an array of regex expressions?
filterTemp = [];
for (i = 0, len = filterWords.length; i < len; i++) {
word = filterWords[i];
filterTemp.push(new RegExp("\b" + word + "\b"));
}
filter = new RegExp(filterTemp.source.join("|"), "gi");
return console.log("filter", filter);
You don't need to construct RegExp inside loop just keep pushing strings into temp array and then use join only once outside to construct RegExp object:
var filterWords = ['abc', 'foo', 'bar'];
var filterTemp = [];
for (i = 0, len = filterWords.length; i < len; i++) {
filterTemp.push("\\b" + filterWords[i] + "\\b");
}
filter = new RegExp(filterTemp.join("|"), "gi");
console.log("filter", filter);
//=> /\babc\b|\bfoo\b|\bbar\b/gi
In 2022:
const validate = (val: string) => {
const errorMessage =
'Enter the time separated by commas. For example: 12:30, 22:00, ... etc.';
const values = val.split(',').map((val: string) => val.trim());
const filter = new RegExp(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/);
const isValid = values.some(
(value: string) => !filter.test(value),
);
return !isValid || errorMessage;
}

Categories

Resources