i have a string i need to convert it into a array of object
const str = "addias (brand|type) sneakers(product) for men(o)"
expected output
let output = [
{
key:"addias",
value:["brand","type"]
},
{
key:"sneakers",
value:["product"]
},
{
key:"for men",
value:[]
}
]
code i tried
function gerateSchema(val) {
let split = val.split(" ")
let maps = split.map((i) => {
let obj = i.split("(")
let key = obj[0].replaceAll(/\s/g, "")
let cleanValue = obj[1].replace(/[{()}]/g, "")
let stripedValues = cleanValue.split("|")
return {
key: key,
value: stripedValues,
}
})
return maps
}
let out = gerateSchema(str)
but this breaking when there is some word with space for example for men
how to do split with a regex
One approach would be first do a regex find all to find all key/value combinations in the original string. Then, iterate that result and build out a hashmap using the word keys and the array values.
var str = "addias (brand|type) sneakers(product) for men(o)";
var matches = str.match(/\w+(?: \w+)*\s*\(.*?\)/g, str);
var array = [];
for (var i=0; i < matches.length; ++i) {
var parts = matches[i].split(/\s*(?=\()/);
var map = {};
map["key"] = parts[0];
map["value"] = parts[1].replace(/^\(|\)$/g, "").split(/\|/);
array.push(map);
}
console.log(array);
The first regex matches each key/value string:
\w+ match a word
(?: \w+)* followed by a space, and another word, the quantity zero or more times
\s* optional whitespace
\( (
.*? pipe separated value string
\) )
Then, we split each term on \s*(?=\(), which is the space(s) immediately preceding the (...|...) term. Finally, we split the value string on pipe | to generate the set of values.
An alternative way could be this.
const str = "addias (brand|type) sneakers(product) for men(o)"
const array = str.split(')').filter(i => i.length).map(i => {
const item = i.split('(');
return {
key: item[0].trim(),
value: item[1].split('|')
}
})
console.log(array)
It may be simpler to use the exec method to iterate over the patterns the regex finds.
const str = 'addias(brand|type|size|color) sneakers(pro) for men(o)';
// The regex looks for an initial group of letters,
// then matches the string inside the parentheses
const regex = /([a-z]+)\(([a-z\|]+)\)/g;
let myArray;
const arr = [];
while ((myArray = regex.exec(str)) !== null) {
// Destructure out the key and the delimited string
const [_, key, ...rest] = myArray;
// `split` on the string found in `rest` first element
const values = rest[0].split('|');
// Finally push a new object into the output array
// (removing "o" for whatever reason)
arr.push({
key,
value: values.filter(v => v !== 'o')
});
}
console.log(arr);
With a little help from regex101.com, derived the following regex expressions and the following code.
([^\(]+)\(([^\)]*)\) which breaks down into
([^\(]+) - capture 1 or more chars up to the first ( as group 1
\( - swallow the left parens
([^\)]*) - capture everything up to the next occurrence of ) as group 2
\) - swallow the right parens
and I was starting to [^|]+ - to parse the text of group 2, but it's actually simpler with a simple split statement.
function generateSchema(str) {
const regex = /([^\(]+)\(([^\)]*)\)/mg; // captures the 'word (word)' pattern
let m;
let output = [];
let obj = {};
while ((m = regex.exec(str)) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++;
}
m.forEach((match, groupIndex) => {
if (groupIndex === 1) {
obj = {};
obj.key = match.trim();
} else if (groupIndex === 2) {
obj.value = match.split('|').map(i=>i.trim());
output.push(obj);
}
});
}
return output;
}
const str = "addidas (brand | type ) sneakers(product) for men(o)";
console.log(generateSchema(str));
Related
I am trying to capitalise the first character of each word and join all words into one string. I have managed to capitalise the first character of each word but cant seem to get .join() to work on the final result
function generateHashtag (str) {
let split = str.split(' ')
for(let i = 0; i < split.length; i++){
let finalResult = split[i].charAt(0).toUpperCase() + split[i].substring(1)
console.log(finalResult.join(''))
}
}
console.log(generateHashtag('Hello my name is')) should return ('HelloMyNameIs')
Achieving this by split is possible. first create an array of divided strings (by the delimiter ' ') and then loop around the array and capitalize the first char using the method toUpperCase and concat the rest of the string without the first letter using slice
function generateHashtag(str) {
let split = str.split(' ');
for (let i = 0; i < split.length; i++) {
split[i] = split[i].charAt(0).toUpperCase() + split[i].slice(1);
}
return split.join('');
}
console.log(generateHashtag('Hello my name is'));
More about split - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split
you can do split[i] = split[i].charAt(0).toUpperCase() + split[i].substring(1) in the loop then outside loop do split.join('')
Basically you are replacing each word (split[i]) with capitalised word. Then in the end join the words.
finalResult is a String, not an Array so there is no join function.
Use this instead :
function generateHashtag (str) {
let arrayWords = str.split(' ')
const titleCasedArray = arrayWords.map(word => titleCaseWord(word))
return titleCasedArray.join('');
}
function titleCaseWord (word){
return word.slice(0,1).toUpperCase() + word.slice(1,-1).toLowerCase()
}
You can do something like this:
function generateHashtag (str) {
//returns array of strings
let split = str.split(' ')
//returns array of strings with each word capitalized
const capitalizedWordsArr = split.map( word => word.charAt(0).toUpperCase() + word.substring(1))
//returns a string by joining above array with no spaces
return capitalizedWordsArr.join('')
}
This is a perfect use-case for Array.prototype.reduce:
function generateHashtag(str) {
return str
.split(' ')
.reduce((acc, [firstLetter, ...rest]) => acc += `${firstLetter.toUpperCase()}${rest.join('')}`,
''
);
}
console.log(generateHashtag('Hello my name is')); // should return ('HelloMyNameIs')
Javascript strings are immutable so you cannot overwrite them on the go, but you can overwrite array elements.
By using String.prototype.substr() you can extract a part of the string, you can use these parts, modify it and create a new string then replace the old array element. finally returning the joined string like you wanted to
function generateHashtag(str) {
const split = str.split(' ') // array of words
for (let i = 0; i < split.length; i++)
split[i] = split[i].substr(0, 1).toUpperCase() + split[i].substr(1); // overwriting existing elements with Titlecased words
return split.join(''); // returning final string
}
console.log(generateHashtag('Hello my name is'))
You don't need to use join at all, just declare and initialize finalResult outside the loop and concatenate each word inside the loop:
function generateHashtag(str) {
const split = str.split(' '); // Array<String>
let finalResult = ''; // String
for(let i = 0; i < split.length; i++) {
const titleCased = split[i].charAt(0).toUpperCase() + split[i].substring(1);
finalResult += titleCased;
}
return finalResult;
}
console.log(generateHashtag('Hello my name is'));
However, you can simplify this code considerably by using a functional-programming (FP) style with map and reduce. See below.
I've also changed your code to use toLocaleUpperCase instead of toUpperCase and uses [0] for brevity.
It's still safe to use substring(1) for single-character strings, it just returns ''.
function generateHashtag(str) {
return ( str
.split(' ')
.map( word => word[0].toLocaleUpperCase() + word.substring(1).toLocaleLowerCase() )
.reduce( ( word, concat ) => concat + word, "" )
);
}
I forgot that join() can still be used instead of reduce (and will have an optimized implementation inside the JS engine anyway):
I've also moved the map function's logic to a named function toTitleCase.
function generateHashtag(str) {
const toTitleCase( word ) => word[0].toLocaleUpperCase() + word.substring(1).toLocaleLowerCase();
return ( str
.split(' ')
.map( word => toTitleCase( word ) ) // or just `.map( toTitleCase )`
.join()
);
}
The return statement has parens to prevent unwanted automatic-semicolon-insertion which would otherwise break the function.
If you want something similar to your code, but working, i would do this:
function generateHashtag (str) {
let split = str.split(' ')
let newStr = []
for (let i = 0; i < split.length; i++){
newStr.push(split[i].charAt(0).toUpperCase() + split[i].substring(1))
}
return newStr.join('')
}
You could also choose to do this task using a 'regular expression'.
https://cheatography.com/davechild/cheat-sheets/regular-expressions/
Here is a quick implementation:
const generateHashtag = str => {
// regular expression to capitalize the words
const regEx = /(\b[a-z](?!\s))/g
str = str.replace(regEx, (char) => {
return char.toUpperCase()
});
// remove spaces, return
return str.split(' ').join('')
}
Same code, but with less readability:
const generateHashtag = str => {
return str.replace(/(\b[a-z](?!\s))/g, (char) => {
return char.toUpperCase()
}).split(' ').join('');
}
function generateHashtag (str) {
return str.replace(/\b\S/g, e => e.toUpperCase()).replace(/\s/g,'');
}
console.log(generateHashtag('Hello my name is'))
\b: bondary \S: non space \s: space.
https://regex101.com/
//try this code solve your problem
const generateHashtag = str => {
let split = str.split(' ')
let finalResult = []
for (word of split) {
finalResult.push(word[0].toUpperCase() + word.substring(1))
}
return finalResult.join('')
}
console.log(generateHashtag('Hello my name is'))
I'm trying to create arrays from the following strings:
"[DHKSGFKDBF][JDKDFJ][NDKDHDLSJD]" // ["DHKSGFKDBF", "JDKDFJ", "NDKDHDLSJD"]
"[ADJDJKDFJ][JEJZJDUDU]" // ["ADJDJKDFJ", "JEJZJDUDU"]
"[ADJDJKDFJ]" // ["ADJDJKDFJ"]
There is a simple way to do this?
There are a bunch of ways to do it. Easiest would be remove the outside one and split.
var str = "[DHKSGFKDBF][JDKDFJ][NDKDHDLSJD]"
var arr = str.slice(1,str.length-1).split('][');
console.log(arr);
var str = "[DHKSGFKDBF][JDKDFJ][NDKDHDLSJD]"
var array = str.substring(1,str.length-1).split("][");
console.log(array);
results into:
["DHKSGFKDBF", "JDKDFJ", "NDKDHDLSJD"]
You can replace all non-alphanumeric characters (/\W+/g) with a delimiter (,), split on the delimiter, and then filter out the nulls (!v) at the beginning and end.
const inputs = [
"[DHKSGFKDBF][JDKDFJ][NDKDHDLSJD]", // ["DHKSGFKDBF","JDKDFJ","NDKDHDLSJD"]
"[ADJDJKDFJ][JEJZJDUDU]", // ["ADJDJKDFJ","JEJZJDUDU"]
"[ADJDJKDFJ]" // ["ADJDJKDFJ"]
]
const convert = s => s.replace(/\W+/g, ',').split(',').filter(v => !!v);
inputs.forEach(input => console.log(JSON.stringify(convert(input))));
.as-console-wrapper { top: 0; max-height: 100% !important; }
You can use string.match function.
var str = "[DHKSGFKDBF][JDKDFJ][NDKDHDLSJD]";
console.log(str.match(/\[(.*?)\]/g).map(item => item.substring(1, item.length - 1)));
Once you have checked the correct format of the string (i.e. starting with [ and ending with ]), you could remove the leading [ and the closing ] and split by ][, e.g.:
[
"[DHKSGFKDBF][JDKDFJ][NDKDHDLSJD]",
"[ADJDJKDFJ][JEJZJDUDU]",
"[ADJDJKDFJ]",
""
].forEach(str => {
console.log(splitStr(str));
});
function splitStr(str) {
if (str && str.length > 2 && str.charAt(0) == '[' && str.charAt(str.length - 1) == ']') {
return str.substring(1, str.length - 1).split('][');
}
return [];
}
I am trying to add a # in front of any words in a string that match a set of 'filter' words in an array.
This is what I have so far
let wordsArray = ['she', 'smile'];
let sentence = 'She has a big smile';
let sentenceArray = sentence.split(" ");
wordsArray.forEach((i, vals) => {
sentenceArray.forEach((j, sVal) => {
if (sVal === vals) {
sentenceArray[j] = `#${j}`;
console.log(sentenceArray)
}
})
});
This is what it is spitting out in the console.
app.js:17 (5) ["She", "has", "a", "big", "smile", She: "#She"]
app.js:17 (5) ["She", "has", "a", "big", "smile", She: "#She", has:
"#has"] app.js:23 She has a big smile
Any ideas on where I am going wrong?
Repl Example
You can use Array.map to iterate through each word in the sentence, then if it matches return the word with a # symbol.
let wordsArray = ['she', 'smile'];
let sentence = 'She has a big smile';
let sentenceArray = sentence.split(" ");
sentenceArray = sentenceArray.map((word) => {
let matchIndex = wordsArray.indexOf(word.toLowerCase())
return (matchIndex !== -1)
? '#'.concat(word)
: word
})
The second parameter of the forEach callback is the index, you're currently iterating over, not the value. You should also call toLowerCase on the word in the sentence to compare against the lower-cased word in the wordsArray:
let wordsArray = ['she', 'smile'];
let sentence = 'She has a big smile';
let sentenceArray = sentence.split(" ");
wordsArray.forEach((vals) => {
sentenceArray.forEach((sVal, j) => {
if (sVal.toLowerCase() === vals) {
sentenceArray[j] = `#${sVal}`;
}
})
});
console.log(sentenceArray)
But rather than a nested loop, constructing a Set of the wordsArray would be less computationally complex (O(n) instead of O(n ^ 2)), in addition to being more elegant:
const wordsArray = ['she', 'smile'];
const wordsSet = new Set(wordsArray);
const sentence = 'She has a big smile';
const result = sentence.split(" ")
.map(word => wordsSet.has(word.toLowerCase()) ? '#' + word : word);
console.log(result);
wordsArray.forEach((word) =>sentence = sentence.replace(new RegExp(word,"ig"),"#"+word))
Iterates over all the words in teh filter then replaces the words in the sentence using a regular expression new RegExp(word, "ig") first argument is the phrase to be matched second argument "ig" is just flags, "i" ignores case sensitivity, "g" searches globally.
I want to determine if string has at least 2 same elements from the array
const array = ["!", "?"];
const string1 = "!hello"; // should return false
const string2 = "!hello?"; // should return false
const string3 = "!hello!"; // should return true
const string4 = "hello ??"; // should return true
const string5 = "hello ?test? foo"; // should return true
const string6 = "hello ?test ?? foo"; // should return true
I'm not sure what is gonna be better: a regex or a function? Any would be fine.
I tried this:
const array = ["!", "?"];
const string = "test!";
array.every(ar => !string.includes(ar));
But it only detects if there at least 1 elements from array, not 2.
You can use Array#some and String#split to do it:
const check=(array,string)=>array.some(char=>(string.split(char).length-1)>=2)
const array = ["!", "?"];
console.log(check(array,"!hello"))
console.log(check(array,"!hello?"))
console.log(check(array,"!hello!"))
console.log(check(array,"hello ??"))
console.log(check(array,"hello ?test? foo"))
console.log(check(array, "hello ?test ?? foo"))
How does it work?
Let's split up (I mean to split() up)!
const check=(array,string)=>
array.some(char=>
(
string.split(char)
.length-1
)>=2
)
First, use Array#some, which tests that at least one element of the array should pass (i.e. either ? or !)
Split up the string by char, and count how many parts do we have
If we have n parts, it means that we have n-1 places where the char matches. (e.g. 2 | splits a string into 3 parts: a|b|c)
Finally, test whether we have 2 or more delimiters
Another way is to use a pattern with a capturing group and a dynamically created character class for [!?] and a backreference \1 to what is captured in group 1 to make sure there are 2 of the same characters present.
([!?]).*\1
Regex demo
For example
const array = ["!", "?"];
const regex = new RegExp("([" + array.join(("")) + "]).*\\1");
[
"!hello",
"!hello?",
"!hello!",
"hello ??",
"hello ?test? foo",
"hello ?test ?? foo"
].forEach(str => console.log(str + ": " + regex.test(str)));
You can use string split and array length like:
const array = ["!", "?"];
const string6 = "hello ?test ?? foo";
var len1 = string6.split(array[0]).length;
var len2 = string6.split(array[1]).length;
if (len>2)||(len2>2)
return true;
EDIT: Using for loop
for (let i=0;i<array.length;i++){
var len = string6.split(array[i]).length;
if (len>2)
return true;
}
return false;
You can follow a very simple solution like below. Split the string using the character in array. check the left of the split operation. If the length is minimum 2, then return true, else false.
Here is a sample jsFiddle: https://jsfiddle.net/sagarag05/qk8f2Lz7/
const array = ["!", "?"];
var str = "How are you!! doing !today?";
function isFound(arr, str){
var isPresent = false;
for(var i=0; i < arr.length; i++){
var res = str.split(arr[i]);
if(res.length-1 >= 2){
isPresent = true;
break;
}
}
return isPresent;
}
isFound(array, str);
Create a function which can be handy for n number of occurrences to find
const arrayData = ["!", "?"];
const strData = "test!";
function checkElements(arr, str, occNum) {
var ctr = 0;
arr.forEach(function (elem) { if(str.includes(elem)) ctr++});
return ctr >= occNum
}
checkElements(arrayData, strData, 2)
Use loop over array and count occurrence then check if occurrence is greater than 1.
function has2(string1, array)
{
for(let i=0;i<array.length;i++)
{
if (string1.split('').reduce(function(n, val) {
return n + (val === array[i]);
}, 0) > 1)
{
return true;
}
}
return false;
}
console.log(has2("!hello!", ["!", "?"])); // true
console.log(has2("!hello?", ["!", "?"])); // false
Here is a regex trick approach. We can try removing all characters from the input which are not part of the character class of characters to find. Then, assert that there are at least two distinct characters remaining in the input.
var input = "!hello?";
input = input.replace(/[^!?]+/g, "");
if (/(.).*(?!\1)./.test(input)) {
console.log("MATCH");
}
else {
console.log("NO MATCH");
}
The logic here is fairly straightforward. Using the input !hello? as an example, we first remove all non marker characters, leaving us with !?. Then, we use a regex to assert that there are at least two distinct characters remaining. This is true for this input, so we print MATCH.
Edit:
To build the regex alternation from your input array use join:
const array = ["!", "?"];
var regex = "[^" + array.join("") + "]+";
There is a much simpler solution for this:
var a = ["!", "?"], s = "!hello!";
a.some(v=>s.split(v).length>2) // (returns true if multiples are found)
We can turn it into a function to test:
const a = ["!", "?"];
function Test(s) { return a.some(v => s.split(v).length > 2) }
const string1 = "!hello"; // should return false
const string2 = "!hello?"; // should return false
const string3 = "!hello!"; // should return true
const string4 = "hello ??"; // should return true
const string5 = "hello ?test? foo"; // should return true
const string6 = "hello ?test ?? foo"; // should return true
console.log(Test(string1), Test(string2), Test(string3), Test(string4),
Test(string5), Test(string6));
> false false true true true true
Note: My code changed a few times and in the end was close to the accepted answer and I didn't realize. That said, you don't need to subtract anything, so that part is unnecessary.
function checkDups(arr, str) {
var ctr = [];
for (var i = 0; i < arr.length; i++) {
var pos = str.indexOf(arr[i]);
var count = 0;
ctr[i] = 0;
while (pos > -1) {
++count;
pos = str.indexOf(arr[i], ++pos);
}
if (count >= 2) {
return true
}
}
return false
}
console.log(checkDups(["!", "?"], "!hello"))
console.log(checkDups(["!", "?"], "!hello?"))
console.log(checkDups(["!", "?"], "!hello!"))
console.log(checkDups(["!", "?"], "hello ??"))
console.log(checkDups(["!", "?"], "hello ?test? foo"))
console.log(checkDups(["!", "?"], "hello ?test ?? foo"))
I'm trying to write a code that finds all words that have 4 (in this case) characters. So my expected output would be this that abcd
My current code looks like this, the problem with it, it's only finding the first word this and it stops looking for further that abcd it.
let string = 'aa this bb cc that abcd';
function longWord(str) {
let words = str.split(' ');
let longWord = '';
for (let word of words) {
if (word.length > longWord.length) longWord = word;
}
return longWord;
}
console.log(longWord(string));
From my understanding i need to include a loop.
Change from for (let word of words) {
To this for (var i = 1; i < words.length; i++) {
But then after this i'm lost :(
Looking at google i'm able to find only code with similar to mine or outputting a char number.
You can use split with filter like so.
split will create an array of strings seperated by space. filter will return only strings with length of 4 characters. Finally, join will return the array as space seperated strings.
let string = 'aa this bb cc that abcd';
function longWord(str) {
return str.split(' ').filter(x => x.length === 4).join(' ');
}
console.log(longWord(string));
string.split(' ').filter(x => x.length === 4).map(x => x)
This piece of code should give you the expected output
There you go
split: create an array with all the words
filter: get only the words with 4 chars
join: create a string with all elements splitted with a space
var el = string.split(' ').filter(x => x.length == 4).join(' ');
If I understand correctly there are two tasks:
1. find the largest character count for a word in the string, and
2. find all the words that match this count
const longestWords = str => {
const words = str.split(/\s+/g);
const maxCount = Math.max.apply( null, words.map( word => word.length ));
return words.filter( word => word.length === maxCount );
};
https://jsfiddle.net/pkxwfmb2/
console.log( longestWords("aa this bb cc that abcd") );
// [ "this", "that", "abcd" ]
console.log( longestWords("aa this bb cc that abcdef") );
// [ "abcdef" ]
console.log( longestWords("a bb c dd e f gg") );
// [ "bb", "dd", "gg" ]
Try this:
let string = 'aa this bb cc that abcd';
function longWord(str) {
let words = str.split(' ');
let output = [];
for (let i = 0; i < words.length; i++) {
if (words[i].length == 4)
output.push(words[i]);
}
return output.join(',');
}
console.log(longWord(string));
You'll get your expected output.