Regex to split string into separate keys - javascript

Working on a bit of an interesting project and I'm having a little trouble writing regex for this.
Using regex and Javascript, I want to split my input string into an array of "keys". Strings that have an invalid key should return 0 keys (null, empty array, whatever is fine).
The "spec"
<input string> => <matches>
# named keys
a => ['a']
b => ['b']
foo[bar] => ['foo', 'bar']
# numeric keys are ok too
a[0] => ['a', '0']
b[1] => ['b', '1']
# 'push' syntax is also valid
push[] => ['push', '']
hello[] => ['hello', '']
# complex!
complex[named][0][] => ['complex', 'named', '0', '']
Invalid examples
# named keys must start with [a-zA-Z]
_invalid => null
3foo[abc] => null
# named keys can include a number as long as they start with a letter
foo3valid[this_is_valid_2] => ['foo3valid', 'this_is_valid_2']
What I have so far
var regex = /^(?:\[?([a-zA-Z][a-zA-Z0-9_]*|[a-zA-Z0-9_]*)\]?)+$/g;
var keys = [];
myInput.replace(regex, function(match,capture,pos,source){
keys.push(capture);
});
console.log(myInput, keys);
How it fails
My regex is matching only the last "key" E.g.,
# should be ['a', 'b', 'c']
a[b][c] => ['c']
Tinker on jsFiddle
jsfiddle

I have taken another approach to get the desired results:
Test whether the given phrase is valid.
If valid, use a global match to create an array.
Code: Fiddle: http://jsfiddle.net/MVJZc/2/
///Example
var myInput = "a[b][][c]";
var validate = /^[a-zA-Z][a-zA-Z0-9_]*(?:\[(?:\d*|[a-zA-Z0-9_]+)\])*$/,
pattern = /[a-zA-Z0-9_]+|(?=\[\])/g,
//(?=\[\]) is used to create an empty entry for []
keys = null;
if (validate.test(myInput)) {
keys = myInput.match(pattern);
}
Note: If you want to extend the pattern, e.g. to include the $ character, modify the first RegEx. The second RegEx (pattern) should at least contain all valid characters.

would something like this work?
var input = "a[b][c]";
input = input.replace(/[\[\]]$/,"").replace(/[\]\[]+/g,"|");
var args = input.split("|");
And here's a demo:
http://jsfiddle.net/vZBtj/

Here's another possibility, definitely not as succinct as Rob W's, but get's the job done ;-)
function parseThing(aStr){
var res = [];
success = false;
if(aStr.indexOf('[') > 0){
var idx = aStr.indexOf('[');
var first = aStr.substring(0, idx);
if(first.match(/\w/)){
success = true;
res.push(first);
//now parse stuff inside brackets
var rest = aStr.substring(idx, aStr.length-1);
var arr = rest.split("]");
for(i in arr){
res.push(arr[i].replace("[", ""));
}
}
} else if(aStr.match(/\w/)){
res.push(aStr);
}
return res;
}
console.log(parseThing("a[a][b]"));
console.log(parseThing("a"));
console.log(parseThing("b"));
console.log(parseThing("foo[bar] "));
console.log(parseThing("a[0]"));
console.log(parseThing("b[1]"));
console.log(parseThing("complex[named][0][]"));

Related

How to filter exact words from a string?

My main objective is to check if the string contains array of words.
If the string contain $ in front of a word, I don't want it to check the string for array of words and want to straight way console.log it.
The problem I'm having is that its not checking for "Exact words".
For example if you put E, the text should show up since, there's no words that contains only 'E' however, its not showing up.
For instance:
const res = `EU, U.S. REACH DEAL TO RESOLVE BOEING-AIRBUS TRADE DISPUTE
$BA`;
const filters = ["E", "OPEC", "Repo"];
if (!filters.some(element => res.includes(element))) {
console.log(res);
}
Another method, I was thinking maybe using the split method and to check for every array item whether it's a filtered word or not.
var res = `EU, U.S. REACH DEAL TO RESOLVE BOEING-AIRBUS TRADE DISPUTE $BA`;
var filters = ["E", "OPEC", "Repo"];
if (filters.some(element => new RegExp('\\b'+element + '\\b').test(res))) {
console.log(res);
}
Use ES6 Array.filter() and arrow functions with expression body:
var words = ['get', 'help', 'set', 'moon', 'class', 'code', 'Get', ];
var letter = 'e';
var word = "get";
const output = words.filter(x=>x.includes(letter));
console.log(output);
const output2 = words.filter(x => x.toLowerCase().includes(word));
console.log(output2);
.split() each string into an array of lower cased strings (delimiter is a space or a comma). If there's more than one string to be filtered, put them into an array
const strings = ['aaa, BBB, ccc', 'vhko nuu', 'String'];
let arrArr = strings.map(str => {
return str.toLowerCase().split(/[,\s]/);
});
// arrArr = [['aaa', 'bbb', 'ccc'], ['vhko', 'nuu'], ['string']]
Then run each string array of arrArr (array of arrays) through .flatMap((strArr, idx)... and each string of each strArr (string array) through .flatMap(str...
return arrArr.flatMap((strArr, idx) => {
return strArr.flatMap(str => {...
For each str (string) use .includes(list) and .startsWith(char) in a ternary to test if str is in the list or if it starts with a specific string (if the third parameter char was passed).
<IF> list.includes(str) ? <TRUE|THEN> [strings[idx]]
: <ELSE IF> char && str.startsWith(char) ? <TRUE|THEN> [strings[idx]]
: <ELSE|THEN> [];
const strs = [
`EU, U.S. REACH DEAL TO RESOLVE BOEING-AIRBUS TRADE DISPUTE $BA`,
`a, b, C, d, E, f`,
`OPEC reaches agreement`,
`This has opec., repo#, and a dollar$ so this should be ignored`
];
const words = ["e", "opec", "repo"];
const wordFilter = (list, strings, char) => {
let arrArr = strings.map(str => {
return str.toLowerCase().split(/[,\s]/);
});
return arrArr.flatMap((strArr, idx) => {
return strArr.flatMap(str => {
return list.includes(str) ? [strings[idx]] :
char && str.startsWith(char) ? [strings[idx]] :
[];
});
});
};
console.log(wordFilter(words, strs, '$'));
I think you can get some idea from this example.click here to see jsFiddle
var words = ['get', 'help', 'set', 'moon', 'class', 'code'];
var letter = 'e';
function find(words, letter) {
letter = letter.split(''); //get it to as object the we can use it in every method as follwing
return words.filter(function(word) { //get pne buy one word in words array
return letter.every(function(char) {
return word.includes(char); // return word including the letter that you request
});
});
}
const output = find(words, letter);
console.log(output);

Match all possible combinations of substring in array

Assuming I have an input string like "auto encoder" and array of strings
const arr = ['autoencoder', 'auto-encoder', 'autoencoder']
I want the input string to match with all three in array.
I created regex
arr.some(word => word.match(/^[a-zA-Z]+(?:(?:\s[a-zA-Z]+)+|(?:\-[a-zA-Z]+)|(?:[a-zA-Z]+))$/))
but seems it doesn't work as if I test it against an array with a single element like ['auto-encoder'] it returns nothing.
How can I achieve desired matching?
function isIncluded(str, arr) {
str = str.replace(/\W/g, '');
return arr.some(e => (e.replace(/\W/g, '') === str));
}
What about replacing special characters (non-word characters) and comparing with the array?
const arr = ['auto encoder', 'autoencoder'];
const fn = (s, a) => a.includes(s.replace(/\W/g, ''));
console.log(fn('auto-encoder', arr));
console.log(fn('a u t o e n c o d e r', arr));
console.log(fn('random', arr));
I think your input is gonna be a string, not an array of strings. You have already the regex, you just need to define a function around it:
function f(word) {
return word.match(/^[a-zA-Z]+(?:(?:\s[a-zA-Z]+)+|(?:\-[a-zA-Z]+)|(?:[a-zA-Z]+))$/);
}
And if you rinput is gonna be an array you can use various array methods:
If you want a boolean answer if some elements match
function f(arr) {
return arr.some(word => word.match(/^[a-zA-Z]+(?:(?:\s[a-zA-Z]+)+|(?:\-[a-zA-Z]+)|(?:[a-zA-Z]+))$/));
}
If you want to know if all elements match use every instead of some
You have other options. For having the array of the matches use filter:
function f(arr) {
return arr.filter(word => word.match(/^[a-zA-Z]+(?:(?:\s[a-zA-Z]+)+|(?:\-[a-zA-Z]+)|(?:[a-zA-Z]+))$/));
}
You could answer your function(s) according to what you want to achieve with each one.

Alphabetically sort array with no duplicates

I'm trying to create a function that takes an array of strings and returns a single string consisting of the individual characters of all the argument strings, in alphabetic order, with no repeats.
var join = ["test"];
var splt = (("sxhdj").split(""))
var sort = splt.sort()
var jn = sort.join("")
join.push(jn)
function removeDuplicates(join) {
let newArr = {};
join.forEach(function(x) { //forEach will call a function once for
if (!newArr[x]) {
newArr[x] = true;
}
});
return Object.keys(newArr);
}
console.log(removeDuplicates(join));
I can not get the current code to work
Check out the comments for the explanation.
Links of interest:
MDN Array.prototype.sort.
MDN Set
var splt = ("sxhdjxxddff").split("")
// You need to use localeCompare to properly
// sort alphabetically in javascript, because
// the sort function actually sorts by UTF-16 codes
// which isn't necessarily always alphabetical
var sort = splt.sort((a, b)=>a.localeCompare(b))
// This is an easy way to remove duplicates
// by converting to set which can't have dupes
// then converting back to array
sort = [...new Set(sort)]
var jn = sort.join("");
console.log(jn);
Something like this :) Hope it helps!
const string = 'aabbccd';
const array = string.split('');
let sanitizedArray = [];
array.forEach(char => {
// Simple conditional to check if the sanitized array already
// contains the character, and pushes the character if the conditional
// returns false
!sanitizedArray.includes(char) && sanitizedArray.push(char)
})
let result = sanitizedArray.join('')
console.log(result);
Try this:
const data = ['ahmed', 'ghoul', 'javscript'];
const result = [...data.join('')]
.filter((ele, i, arr) => arr.lastIndexOf(ele) === i)
.sort()
.join('');
console.log(result)
There are probably better ways to do it, one way is to map it to an object, use the keys of the object for the used letters, and than sorting those keys.
const words = ['foo', 'bar', 'funky'];
const sorted =
Object.keys(
([...words.join('')]) // combine to an array of letters
.reduce((obj, v) => obj[v] = 1 && obj, {}) // loop over and build hash of used letters
).sort() //sort the keys
console.log(sorted.join(''))

JS match string from right to left

Let's say I have the following object:
const obj = {
'myApi': ['keyOne', 'keyTwo'],
'myApi.keyOne': ['three', 'four'],
'myApi.keyTwo': [...],
'myApi.keyOne.three': [...]
'myApi.keyOne.four': [...]
}
Now, based on the following string:
const str = "if(myApi.keyOne.three"
I want to match the correct object key, but from right to left. So, in the above example, I want to get obj["myApi.keyOne.three"].
indexOf or str.test methods will not work because they will catch myApi and myApi.keyOne also.
Note that it could be any string, the str is just an example. For example:
while(myApi.keyOne.three) {} // should match obj["myApi.keyOne.three"]
if(myApi.keyOne) {} // should match obj["myApi.keyOne"]
etc.
How can I do this?
To get the key, use a regular expression to match myApi, followed by any number of repeated groups of (a period followed by word characters). Then, you can just access the appropriate key on the object:
const obj = {
'myApi': ['keyOne', 'keyTwo'],
'myApi.keyOne': ['three', 'four'],
'myApi.keyOne.three': ['foobar']
};
function getVal(str) {
const key = str.match(/myApi(?:\.\w+)*/)[0];
console.log(obj[key]);
return obj[key];
}
getVal("if(myApi.keyOne.three");
getVal("while(myApi.keyOne.three) {}");
getVal("if(myApi.keyOne) {}");
Search for the key entry in the pattern:
var result = “”;
Object.keys(obj).forEach(function(key) {
if (str.indexOf(key) !== -1 && key.length > result.length) result = key;
});
console.log(obj[result]);
To make things more dynamic (even if there's no guarantee about myApi):
function findStuff(str, obj) {
const keys = Object.keys(obj);
keys.sort((a, b) => b.length - a.length);
// https://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
const re = new RegExp(keys.map(key => key.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')).join('|'));
const match = str.match(re);
return match && match[0];
}
const obj = {
'myApi': ['keyOne', 'keyTwo'],
'myApi.keyOne': ['three', 'four'],
'myApi.keyTwo': [""],
'myApi.keyOne.three': ["THREE"],
'myApi.keyOne.four': [""]
}
console.log(findStuff('while(myApi.keyOne.three) {}', obj));
We take all the keys from the object, then sort them by descending length (so the longest will be matched first). Then regexp-escape them and stick them together in a regexp alternation.
You can use a regular expression like
myApi(\.\w+)*
Details:
myApi - matches the "myApi" string
(\.\w+)* - matches 0 or more repetitions of those alphanumeric strings which follows a period (.) character
const obj = {
'myApi': ['keyOne', 'keyTwo'],
'myApi.keyOne': ['three', 'four'],
'myApi.keyTwo': ['someVlaue1'],
'myApi.keyOne.three': ['someVlaue2'],
'myApi.keyOne.four': ['someVlaue3']
}
var str1 = 'while(myApi.keyOne.three) {}';
var str2 = 'if(myApi.keyOne) {}';
var regex = /myApi(\.\w+)*/g;
var val1 = obj[ str1.match(regex)[0] ];
var val2 = obj[ str2.match(regex)[0] ];
console.log(val1);
console.log(val2);

Remove Whitespace-only Array Elements

Since using array.splice modifies the array in-place, how can I remove all whitespace-only elements from an array without throwing an error? With PHP we have preg_grep but I am lost as to how and do this correctly in JS.
The following will not work because of above reason:
for (var i=0, l=src.length; i<l; i++) {
if (src[i].match(/^[\s\t]{2,}$/) !== null) src.splice(i, 1);
}
Error:
Uncaught TypeError: Cannot call method 'match' of undefined
A better way to "remove whitespace-only elements from an array".
var array = ['1', ' ', 'c'];
array = array.filter(function(str) {
return /\S/.test(str);
});
Explanation:
Array.prototype.filter returns a new array, containing only the elements for which the function returns true (or a truthy value).
/\S/ is a regex that matches a non-whitespace character. /\S/.test(str) returns whether str has a non-whitespace character.
Another filter based variation - reusable (function)
function removeWhiteSpaceFromArray(array){
return array.filter((item) => item != ' ');
}
const words = ['spray', 'limit', 'elite', 'exuberant', ' ', ''];
const result = words.filter(word => word.trim().length > 0);
console.log(result);
a="remove white spaces"
a.split(' ').join('').split('');
It returns an array of all characters in {a="remove white spaces"} with no 'space' character.
You can test the output separately for each method: split() and join().
You removed an item from the array which reduced the array's length. Your loop continued, skipped some indexes (those which were down-shifted into the removed index), and eventually attempted to access an index outside of the new range.
Try this instead:
var src = ["1"," ","2","3"];
var i = src.length;
while(i--) !/\S/.test(src[i]) && src.splice(i, 1);
console.log(src);
And for a new generation (namely ES2015):
['1', ' ', 'c'].filter(item => item.trim() !== '')
More on trim()
Just simply do this example
// this is you array
let array = ["foo","bar","",""]
// remove blanks in array
let array_new_value = array.join(" ").trim().split(' ');
// Print to know if really works
console.log(array_new_value);
I hope this helps you!!
Here's another approach.
var data = JSON.parse(arrayval.split(/\s/).join(''));
function clearSpace(arr){
for (var key in arr) {
if (arr[key] == "") {
arr.splice(key, 1)
clearSpace(arr)
}
}
}
var arr = ["","a","b","",""]
clearSpace(arr)
//I hope this helps you!!
//Vu Tien Luong - 3GTEL

Categories

Resources