Determine if string has at least 2 same elements from an array - javascript

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"))

Related

how to do array split with regex?

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));

Why is my Palindrome code running true for every scenario?

This code returns true for every scenario; it returns true if it's a palindrome and it returns true if it's not a palindrome.
function isPalindrome(string){
string = string.toLowerCase();
var charactersArr = string.split(' ');
var validCharacters = 'abcdefghijklmnopqrstuvwxyz'.split(' ');
var lettersArr = [];
charactersArr.forEach(char => {
if (validCharacters.indexOf(char)> - 1) lettersArr.push(char);
});
return lettersArr.join(' ') === lettersArr.reverse().join(' ');
}
isPalindrome("Taco Cat");
function palindrome(str) {
var re = /[^A-Za-z0-9]/g;
str = str.toLowerCase().replace(re, '');
var len = str.length;
for (var i = 0; i < len/2; i++) {
if (str[i] !== str[len - 1 - i]) {
return false;
}
}
return true;
}
Use this code for checking palindromes . You are complicating your code by passing in var validCharacters = 'abcdefghijklmnopqrstuvwxyz'.split(' ');
You could use re like I have to include all possible combinations including numbers !
It always returns true because lettersArr is always empty. Try to debug your code using debugger or just use console.log for this purpose.
I believe at least this line var charactersArr = string.split(' '); is not doing what is expected: it splits a sentence by space characters into words instead of splitting into separate letters.
It's because lettersArris always empty .... Try this code:
function isPalindrome(string) {
let reversed = string.toLowerCase().split('').reverse().join('');
return reversed === string.toLowerCase()
}
let result1 = isPalindrome("Foo bar")
let result2 = isPalindrome("mom")
console.log(result1, result2)
lettersArr is not what you expect.
You are splitting the input string by spaces, which means you get ['taco', 'cat'] and not ['t', 'a', 'c', 'o', 'c', 'a', 't'].
Then when this line runs:
if (validCharacters.indexOf(char)> - 1) lettersArr.push(char);
char is 'taco' which isn't found in validCharacters, so nothing gets pushed into lettersArr.
And since lettersArr is empty:
lettersArr.join(' ') === lettersArr.reverse().join(' ');
Becomes:
[].join(' ') === [].reverse().join(' ')
Which, of course, it always true.
To fix it you simply want to split the string by an empty string, which gives you array of single character strings.
function isPalindrome(string){
string = string.toLowerCase();
var charactersArr = string.split('');
var validCharacters = 'abcdefghijklmnopqrstuvwxyz'.split('');
var lettersArr = [];
charactersArr.forEach(char => {
if (validCharacters.indexOf(char)> - 1) lettersArr.push(char);
});
return lettersArr.join(' ') === lettersArr.reverse().join(' ');
}
console.log("Taco Cat", isPalindrome("Taco Cat"));
console.log("abc", isPalindrome("abc"));

How to find ALL word that have X amount of characters?

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.

check if the string is a combination of strings in a array using javascript

I have a requirement to check if a string is formed with any combination of strings given in a array
example : we have array of ["for","car","keys","forth"] and a string of "forthcarkeys", the result should be true.If the string is "forthcarxykeys", the result should be false as xy is not in the array. Order of words in array doesnt matter.
its not necessary that all strings from array should be matched but the teststring should only be made of any of the strings in array. If it contain any string other than the ones in array, return false
my approach:
var str = "forthcarkeys";
var arr = ["for","car","keys","forth"];
for(var i=0;i<arr.length;i++)
{
if(str.indexOf(arr[i]) !== -1)
{
str.replace(arr[i],"");
}
}
if(str !== "")
{
console.log("yes");
} else
{
console.log("no");
}
but this approach is not efficient and failing.
One possible approach is to check for each prefix whether it can be expressed using the input strings.
The idea is that if we can easily compute whether the prefix with length i can be expressed with the input strings, if we already have this information for shorter prefixes (this can be done by checking whether any of the allowed strings lead to a shorter expressible prefix) -- see code below.
var str = "forthcarkeys";
var arr = ["for","car","keys","forth"];
// isPossible[i] indicates whether we can express the
// prefix str.substring(0, i) using strings in arr.
var isPossible = Array(str.length + 1).fill(false);
// it is always possible to construct the empty string
isPossible[0] = true;
// consider each prefix of str, from shortest to longest
for (var i = 1; i <= str.length; i++) {
// try to reach this prefix using an allowed string s_allowed,
// by appending s_allowed to a shorter prefix
for (var j = 0; j < arr.length; j++) {
// "start" is the position where the current string
// would need to be appended
start = i - arr[j].length;
if (start >= 0 && isPossible[start]) {
if (str.substring(start, i) == arr[j]) {
isPossible[i] = true;
// we break the loop over j, because we already found a
// solution to express the current prefix str.substring(0,i)
break;
}
}
}
}
for (var i = 1; i <= str.length; i++) {
console.log(str.substring(0, i) + " - " + isPossible[i] + "\n")
}
if (isPossible[str.length]) {
console.log("yes");
} else {
console.log("no");
}
To further detail how this works, consider a smaller example:
str = "abcd"
arr = ["ab", "a", "cd"]
The approach described here tests all prefixes of str, in increasing order of their length:
Step 0: empty prefix -- this is considered as always fine (it can be expressed using 0 strings).
Step 1: prefix "a":
We try reaching this prefix using a shorter prefix + one allowed string. To do so, we iterate through the allowed strings:
"ab" can not be appended to a shorter prefix in order to get "a" (because the start position would need to be -1).
"a" can be appended to the empty prefix, which is always fine -- thus we get that prefix "a" is fine (can be expressed using the allowed strings).
Step 2: prefix "ab":
We try reaching this prefix using a shorter prefix + one allowed string. To do so, we iterate through the allowed strings:
"ab" can be appended to the empty prefix, which is always fine -- thus we get that prefix "ab" is fine (can be expressed using the allowed strings).
Step 3: prefix "abc":
We try reaching this prefix using a shorter prefix + one allowed string. To do so, we iterate through the allowed strings:
"ab" -- to append this one to a shorter prefix and get the current "abc" prefix, we would need to start at position 1, but the substring from that start position is "bc", thus we can not append the string "ab" to get prefix "abc".
"a" -- similar to above, we can not append "a" to get prefix "abc".
"cd" -- similar to above, we can not append "cd" to get prefix "abc".
We exhausted all allowed strings, thus prefix "abc" can not be expressed using the allowed strings.
Step 4: prefix "abcd" (entire string):
We try reaching this prefix using a shorter prefix + one allowed string. To do so, we iterate through the allowed strings:
"ab" -- to append this one to a shorter prefix and get the current "abcd" prefix, we would need to start at position 2, but the substring from that start position is "cd", thus we can not append the string "ab" to get prefix "abcd".
"a" -- similar to above, we can not append "a" to get prefix "abcd".
"cd" -- we can append this allowed string to the "ab" prefix. In a previous step we found out that the "ab" prefix is fine (can be expressed with given strings), thus it is fine to append "cd" from there.
Thus, we get that the prefix "abcd" (which corresponds to the entire string) can be expressed using the input strings.
you need sort array by length Dec order and change if condition for yes is str==""
function check(str){
var arr = ["for", "car", "keys", "forth"];
arr= arr.sort((a,b)=> b.length-a.length) // sort with length dec order
console.log(arr) //longest length string is first then to lower length
for (var i = 0; i < arr.length; i++) {
str = str.replace(arr[i], "");
}
if (str.trim()== "") { //empty
console.log("yes");
} else {
console.log("no");
}
}
check("forthcarkeys")
check("forthcarxykeys")
If all words are matched first, non occurring words can be omitted at the start. Then if the matches are in order of occurrence in the string, recursion can be used to find all matches after the match:
function eval(str, wordList = ["for","car","keys","forth", "the"]){ //note, added 'the' for testing
if(!str)return false; //empty string -> false
const words = wordList.map(w=> ({word:w, index: str.indexOf(w)})) //map all words with their occurence index inside the string
.filter(w=>w.index !== -1) //get rid of non occuring words alltogether
.sort((w1,w2) => w1.index - w2.index); //sort by index of occurence
const check = (arr,ind) => {
if(ind>=str.length)return ind === str.length; //end of string reached -> match if exactly at end (false if greater than)
let w;
while(arr.length){
[w,...arr] = arr; //destructure: w = next word (index 0), arr is set to the remaining elements
if(w.index > ind) return false; //gap since last match -> no match
if(w.index===ind && check(arr,ind + w.word.length)) //if match is at the expected index, check the next indices
return true; //word started at the 'current' index and remaining words match as well
//if code arrives here, try further with next word (while)
}
return false;
};
return check(words,0); //start recursive function with all words at string index 0
}
//test
function test(str, words){
console.log(str,':', eval(str, words));
}
test("forthcarkeys");
test("forthcarxykeys");
test("forthecar");
test("abcdef",[ "abc", "def", "abcd" ]);
You could make an extended try and search for every words and use the temporary result set for filtering out if the words are in the string.
function check(string, array) {
function fork(i, t) {
var s = t.slice(), j;
if (i === possibilities.length) {
result.push(t.join(''));
return;
}
if (possibilities[i].word.split('').every(function (c, j) { return s[j + possibilities[i].position] !== ''; })) {
for (j = 0; j < possibilities[i].word.length; j++) {
s[j + possibilities[i].position] = ''
}
}
fork(i + 1, s);
fork(i + 1, t);
}
var possibilities = array.reduce(function (r, a) {
var p = string.indexOf(a);
while (p !== -1) {
r.push({ word: a, position: p });
p = string.indexOf(a, p + 1);
}
return r;
}, []),
result = [];
console.log(possibilities);
fork(0, string.split(''));
console.log(result);
return result.some(function (a) { return !a; });
}
console.log(check("forthcarkeyboardingfor", ["for", "car", "key", "forth", "keyboard", "boarding"])); // true
console.log(check("forthxycarkeyboardingfor", ["for", "car", "key", "forth", "keyboard", "boarding"])); // false
.as-console-wrapper { max-height: 100% !important; top: 0; }
Version as above with early exit.
function check(string, array) {
function fork(i, t) {
var s = t.slice(), j;
if (i === possibilities.length) {
return !t.join('');
}
if (possibilities[i].word.split('').every(function (c, j) { return s[j + possibilities[i].position] !== ''; })) {
for (j = 0; j < possibilities[i].word.length; j++) {
s[j + possibilities[i].position] = '';
}
}
return fork(i + 1, s) || fork(i + 1, t);
}
var possibilities = array.reduce(function (r, a) {
var p = string.indexOf(a);
while (p !== -1) {
r.push({ word: a, position: p });
p = string.indexOf(a, p + 1);
}
return r;
}, []);
return fork(0, string.split(''));
}
console.log(check("forthcarkeyboardingfor", ["for", "car", "key", "forth", "keyboard", "boarding"])); // true
console.log(check("forthxycarkeyboardingfor", ["for", "car", "key", "forth", "keyboard", "boarding"])); // false
Here is a more robust function that finds all the possible elements and ways that were used to compose it. If the length of the result is zero, then the original text cannot be made from the pool.
function decompose(orignal, pool) { // recurisve function to find combinations of text
var results = [];
for (var element of pool) { // for each element in pool
if (orignal == element) { // resursive base case, stop when orignal == element
results.push([element]); // * add solution
} else {
if (orignal.indexOf(element) == 0) { // if original text starts with element
var remaining = orignal.slice(element.length); // ready remaining text to be scanned
var subresults = decompose(remaining, pool); // recursive call: findCombinationsOf remaining
for (subresult of subresults) {
results.push([element].concat(subresult)); // * add solution
}
}
}
}
return results;
}
console.log(JSON.stringify(decompose("forthcarkeys", ["for","car","keys","forth"])));
console.log(JSON.stringify(decompose("forthcarkeys", ["for","car","keys","forth", "th"])));
console.log(JSON.stringify(decompose("nowaydude!", ["for","car","keys","forth", "th"])));
Here's my solution
Details:
transform the given string "forthcarxykeys" into an array and assign it to a variable chars
iterate over the given array ["for","car","keys","forth"]
per every iteration, check if the word ( i.e. "for") exists in the array
if it exists, obtain the index for each letter found and mark it as true in the char array
return true if all values in chars are true, false if not.
JS:
// arr = ["for", "car", "keys", "forth"];
// str = "forthcarxykeys";
function check(arr, str) {
let chars = str.split('');
for (let i = 0; i < arr.length; i++) {
let word = arr[i];
let index = str.indexOf(word);
let wordExists = index !== -1;
if (wordExists) {
let endIndex = index + word.length;
for (index; index < endIndex; index++) {
chars[index] = true;
}
}
}
return chars.every(i => i === true);
}
Here is updated code. String replace wasn't working, hence used regex to achieve that. var re = new RegExp(arr[i], 'g');
function check(str, arr) {
var flag = true;
for (var i = 0; i < arr.length; i++) {
if (str.indexOf(arr[i]) === -1) {
flag = false;
}
}
if (!flag) {
console.log("Some keys didn't matched.");
} else {
console.log("Nothing remains. All matched.");
}
}
var str = "forcarxy";
var arr = ["for", "car", "keys", "forth"];
check(str, arr);
var arr = ["abcd", "def", "abc"];
var str = "abcdef";
check(str, arr);
var arr = [ "abcd", "cdef" ];
var str = "abcdef";
check(str, arr);
var str = "aabc";
var arr = ["a", "bc"];
check(str, arr);
Code has been updated to consider the commented case by #inetphantom

Javascript How to split string by symbols count [duplicate]

As the title says, I've got a string and I want to split into segments of n characters.
For example:
var str = 'abcdefghijkl';
after some magic with n=3, it will become
var arr = ['abc','def','ghi','jkl'];
Is there a way to do this?
var str = 'abcdefghijkl';
console.log(str.match(/.{1,3}/g));
Note: Use {1,3} instead of just {3} to include the remainder for string lengths that aren't a multiple of 3, e.g:
console.log("abcd".match(/.{1,3}/g)); // ["abc", "d"]
A couple more subtleties:
If your string may contain newlines (which you want to count as a character rather than splitting the string), then the . won't capture those. Use /[\s\S]{1,3}/ instead. (Thanks #Mike).
If your string is empty, then match() will return null when you may be expecting an empty array. Protect against this by appending || [].
So you may end up with:
var str = 'abcdef \t\r\nghijkl';
var parts = str.match(/[\s\S]{1,3}/g) || [];
console.log(parts);
console.log(''.match(/[\s\S]{1,3}/g) || []);
If you didn't want to use a regular expression...
var chunks = [];
for (var i = 0, charsLength = str.length; i < charsLength; i += 3) {
chunks.push(str.substring(i, i + 3));
}
jsFiddle.
...otherwise the regex solution is pretty good :)
str.match(/.{3}/g); // => ['abc', 'def', 'ghi', 'jkl']
Building on the previous answers to this question; the following function will split a string (str) n-number (size) of characters.
function chunk(str, size) {
return str.match(new RegExp('.{1,' + size + '}', 'g'));
}
Demo
(function() {
function chunk(str, size) {
return str.match(new RegExp('.{1,' + size + '}', 'g'));
}
var str = 'HELLO WORLD';
println('Simple binary representation:');
println(chunk(textToBin(str), 8).join('\n'));
println('\nNow for something crazy:');
println(chunk(textToHex(str, 4), 8).map(function(h) { return '0x' + h }).join(' '));
// Utiliy functions, you can ignore these.
function textToBin(text) { return textToBase(text, 2, 8); }
function textToHex(t, w) { return pad(textToBase(t,16,2), roundUp(t.length, w)*2, '00'); }
function pad(val, len, chr) { return (repeat(chr, len) + val).slice(-len); }
function print(text) { document.getElementById('out').innerHTML += (text || ''); }
function println(text) { print((text || '') + '\n'); }
function repeat(chr, n) { return new Array(n + 1).join(chr); }
function textToBase(text, radix, n) {
return text.split('').reduce(function(result, chr) {
return result + pad(chr.charCodeAt(0).toString(radix), n, '0');
}, '');
}
function roundUp(numToRound, multiple) {
if (multiple === 0) return numToRound;
var remainder = numToRound % multiple;
return remainder === 0 ? numToRound : numToRound + multiple - remainder;
}
}());
#out {
white-space: pre;
font-size: 0.8em;
}
<div id="out"></div>
If you really need to stick to .split and/or .raplace, then use /(?<=^(?:.{3})+)(?!$)/g
For .split:
var arr = str.split( /(?<=^(?:.{3})+)(?!$)/ )
// [ 'abc', 'def', 'ghi', 'jkl' ]
For .replace:
var replaced = str.replace( /(?<=^(?:.{3})+)(?!$)/g, ' || ' )
// 'abc || def || ghi || jkl'
/(?!$)/ is to not stop at end of the string. Without it's:
var arr = str.split( /(?<=^(?:.{3})+)/ )
// [ 'abc', 'def', 'ghi', 'jkl' ] // is fine
var replaced = str.replace( /(?<=^(.{3})+)/g, ' || ')
// 'abc || def || ghi || jkl || ' // not fine
Ignoring group /(?:...)/ is to prevent duplicating entries in the array. Without it's:
var arr = str.split( /(?<=^(.{3})+)(?!$)/ )
// [ 'abc', 'abc', 'def', 'abc', 'ghi', 'abc', 'jkl' ] // not fine
var replaced = str.replace( /(?<=^(.{3})+)(?!$)/g, ' || ' )
// 'abc || def || ghi || jkl' // is fine
My solution (ES6 syntax):
const source = "8d7f66a9273fc766cd66d1d";
const target = [];
for (
const array = Array.from(source);
array.length;
target.push(array.splice(0,2).join(''), 2));
We could even create a function with this:
function splitStringBySegmentLength(source, segmentLength) {
if (!segmentLength || segmentLength < 1) throw Error('Segment length must be defined and greater than/equal to 1');
const target = [];
for (
const array = Array.from(source);
array.length;
target.push(array.splice(0,segmentLength).join('')));
return target;
}
Then you can call the function easily in a reusable manner:
const source = "8d7f66a9273fc766cd66d1d";
const target = splitStringBySegmentLength(source, 2);
Cheers
const chunkStr = (str, n, acc) => {
if (str.length === 0) {
return acc
} else {
acc.push(str.substring(0, n));
return chunkStr(str.substring(n), n, acc);
}
}
const str = 'abcdefghijkl';
const splittedString = chunkStr(str, 3, []);
Clean solution without REGEX
My favorite answer is gouder hicham's. But I revised it a little so that it makes more sense to me.
let myString = "Able was I ere I saw elba";
let splitString = [];
for (let i = 0; i < myString.length; i = i + 3) {
splitString.push(myString.slice(i, i + 3));
}
console.log(splitString);
Here is a functionalized version of the code.
function stringSplitter(myString, chunkSize) {
let splitString = [];
for (let i = 0; i < myString.length; i = i + chunkSize) {
splitString.push(myString.slice(i, i + chunkSize));
}
return splitString;
}
And the function's use:
let myString = "Able was I ere I saw elba";
let mySplitString = stringSplitter(myString, 3);
console.log(mySplitString);
And it's result:
>(9) ['Abl', 'e w', 'as ', 'I e', 're ', 'I s', 'aw ', 'elb', 'a']
try this simple code and it will work like magic !
let letters = "abcabcabcabcabc";
// we defined our variable or the name whatever
let a = -3;
let finalArray = [];
for (let i = 0; i <= letters.length; i += 3) {
finalArray.push(letters.slice(a, i));
a += 3;
}
// we did the shift method cause the first element in the array will be just a string "" so we removed it
finalArray.shift();
// here the final result
console.log(finalArray);
var str = 'abcdefghijkl';
var res = str.match(/.../g)
console.log(res)
here number of dots determines how many text you want in each word.
function chunk(er){
return er.match(/.{1,75}/g).join('\n');
}
Above function is what I use for Base64 chunking. It will create a line break ever 75 characters.
Here we intersperse a string with another string every n characters:
export const intersperseString = (n: number, intersperseWith: string, str: string): string => {
let ret = str.slice(0,n), remaining = str;
while (remaining) {
let v = remaining.slice(0, n);
remaining = remaining.slice(v.length);
ret += intersperseWith + v;
}
return ret;
};
if we use the above like so:
console.log(splitString(3,'|', 'aagaegeage'));
we get:
aag|aag|aeg|eag|e
and here we do the same, but push to an array:
export const sperseString = (n: number, str: string): Array<string> => {
let ret = [], remaining = str;
while (remaining) {
let v = remaining.slice(0, n);
remaining = remaining.slice(v.length);
ret.push(v);
}
return ret;
};
and then run it:
console.log(sperseString(5, 'foobarbaztruck'));
we get:
[ 'fooba', 'rbazt', 'ruck' ]
if someone knows of a way to simplify the above code, lmk, but it should work fine for strings.
Coming a little later to the discussion but here a variation that's a little faster than the substring + array push one.
// substring + array push + end precalc
var chunks = [];
for (var i = 0, e = 3, charsLength = str.length; i < charsLength; i += 3, e += 3) {
chunks.push(str.substring(i, e));
}
Pre-calculating the end value as part of the for loop is faster than doing the inline math inside substring. I've tested it in both Firefox and Chrome and they both show speedup.
You can try it here
Here's a way to do it without regular expressions or explicit loops, although it's stretching the definition of a one liner a bit:
const input = 'abcdefghijlkm';
// Change `3` to the desired split length.
const output = input.split('').reduce((s, c) => {
let l = s.length-1;
(s[l] && s[l].length < 3) ? s[l] += c : s.push(c);
return s;
}, []);
console.log(output); // output: [ 'abc', 'def', 'ghi', 'jlk', 'm' ]
It works by splitting the string into an array of individual characters, then using Array.reduce to iterate over each character. Normally reduce would return a single value, but in this case the single value happens to be an array, and as we pass over each character we append it to the last item in that array. Once the last item in the array reaches the target length, we append a new array item.
Some clean solution without using regular expressions:
/**
* Create array with maximum chunk length = maxPartSize
* It work safe also for shorter strings than part size
**/
function convertStringToArray(str, maxPartSize){
const chunkArr = [];
let leftStr = str;
do {
chunkArr.push(leftStr.substring(0, maxPartSize));
leftStr = leftStr.substring(maxPartSize, leftStr.length);
} while (leftStr.length > 0);
return chunkArr;
};
Usage example - https://jsfiddle.net/maciejsikora/b6xppj4q/.
I also tried to compare my solution to regexp one which was chosen as right answer. Some test can be found on jsfiddle - https://jsfiddle.net/maciejsikora/2envahrk/. Tests are showing that both methods have similar performance, maybe on first look regexp solution is little bit faster, but judge it Yourself.
var b1 = "";
function myFunction(n) {
if(str.length>=3){
var a = str.substring(0,n);
b1 += a+ "\n"
str = str.substring(n,str.length)
myFunction(n)
}
else{
if(str.length>0){
b1 += str
}
console.log(b1)
}
}
myFunction(4)
function str_split(string, length = 1) {
if (0 >= length)
length = 1;
if (length == 1)
return string.split('');
var string_size = string.length;
var result = [];
for (let i = 0; i < string_size / length; i++)
result[i] = string.substr(i * length, length);
return result;
}
str_split(str, 3)
Benchmark: http://jsben.ch/HkjlU (results differ per browser)
Results (Chrome 104)

Categories