I have a large string that comes across the wire using an $.ajax request. I can format the string any way necessary, and currently am using a % as the line delimiter and , as the item delimiter. Considering performance is so essential in my application, does anyone have a quicker way to do the following? Thank You
function convertCSV(s) {
var lines = s.split("%");
var items, sym, arr = [];
for (var x = 0, len = lines.length; x < len; x++) {
items = lines[x].split(",");
sym = {};
sym.time = +items[0];
sym.num1 = +items[1];
sym.num2 = +items[2];
sym.a1 = +items[3];
sym.b1 = +items[4];
sym.c1 = +items[5];
sym.d1 = +items[6];
sym.e1 = +items[7];
sym.f1 = +items[8];
sym.g1 = +items[9];
sym.h1 = +items[10];
sym.l1 = +items[11];
arr[x] = sym;
}
return arr;
}
Perhaps JSON encode what you send over the wire and then JSON decode once you receive it.
A (minor) optimisation:
function convertCSV(s) {
var lines = s.split("%");
var items, arr = [];
while ((items = lines.shift()) && (items = items.split(",")) {
arr.push({
time : +items[0], num1 : +items[1], num2 : +items[2],
a1 : +items[3], b1 : +items[4], c1 : +items[5],
d1 : +items[6], e1 : +items[7], f1 : +items[8],
g1 : +items[9], h1 : +items[10], l1 : +items[11]
});
}
return arr;
}
Could be worth experimenting with Array.shift() and Array.pop()
If you are concerned about speed, you should probably create a simply parser that will parse the string character by character.
Here's a simple example:
DEMO
function convertCSV(s, properties) {
var result = [],
i = 0,
len = s.length,
propIndex = 0,
row = {},
val = '',
c;
for (; i < len; i++) {
switch(c = s[i]) {
case ',':
row[properties[propIndex++]] = val;
val = '';
break;
case '%':
result.push(row);
row[properties[propIndex++]] = val;
propIndex = 0;
row = {};
val = '';
break;
default:
val += c;
}
}
return result;
}
console.log(convertCSV('a,b,c%d,e,f%h,i,j%', ['a', 'b', 'c']));
EDIT:
I ran a few performance tests and it seems that I wasn`t right afterall. Your current method is actually the second fastest, but the quickest way to do this seems to be using regular expressions. I must say that I am quite surprised that the simple parser isin't the fastest solution.
PERFORMANCE TEST
var rx = /(.*?),(.*?),(.*?)%/g,
result = [],
match;
while (match = rx.exec(s)) {
result.push({
a: match[1],
b: match[2],
c: match[3]
});
}
console.log(result);
Related
this is a leetcode question.
Given two strings s and t which consist of only lowercase letters.
String t is generated by random shuffling string s and then add one more letter at a random position.
Find the letter that was added in t.
https://leetcode.com/problems/find-the-difference/
I'm trying to think a hash table way but seems I m wrong.
var findTheDifference = function(s, t) {
var hashTable = {};
var array = s.split('');
array.forEach(function (element) {
hashTable[element] = element;
});
for( var i = 0; i < t.length; i++) {
if(!hashTable.hasOwnProperty(t.charAt(i))) {
return t.charAt(i);
}
}
};
this wrong code can pass the sample Input:
Input:
s = "abcd"
t = "abcde"
Output:
e
but for large strings will be wrong
Input :"ymbgaraibkfmvocpizdydugvalagaivdbfsfbepeyccqfepzvtpyxtbadkhmwmoswrcxnargtlswqemafandgkmydtimuzvjwxvlfwlhvkrgcsithaqlcvrihrwqkpjdhgfgreqoxzfvhjzojhghfwbvpfzectwwhexthbsndovxejsntmjihchaotbgcysfdaojkjldprwyrnischrgmtvjcorypvopfmegizfkvudubnejzfqffvgdoxohuinkyygbdzmshvyqyhsozwvlhevfepdvafgkqpkmcsikfyxczcovrmwqxxbnhfzcjjcpgzjjfateajnnvlbwhyppdleahgaypxidkpwmfqwqyofwdqgxhjaxvyrzupfwesmxbjszolgwqvfiozofncbohduqgiswuiyddmwlwubetyaummenkdfptjczxemryuotrrymrfdxtrebpbjtpnuhsbnovhectpjhfhahbqrfbyxggobsweefcwxpqsspyssrmdhuelkkvyjxswjwofngpwfxvknkjviiavorwyfzlnktmfwxkvwkrwdcxjfzikdyswsuxegmhtnxjraqrdchaauazfhtklxsksbhwgjphgbasfnlwqwukprgvihntsyymdrfovaszjywuqygpvjtvlsvvqbvzsmgweiayhlubnbsitvfxawhfmfiatxvqrcwjshvovxknnxnyyfexqycrlyksderlqarqhkxyaqwlwoqcribumrqjtelhwdvaiysgjlvksrfvjlcaiwrirtkkxbwgicyhvakxgdjwnwmubkiazdjkfmotglclqndqjxethoutvjchjbkoasnnfbgrnycucfpeovruguzumgmgddqwjgdvaujhyqsqtoexmnfuluaqbxoofvotvfoiexbnprrxptchmlctzgqtkivsilwgwgvpidpvasurraqfkcmxhdapjrlrnkbklwkrvoaziznlpor"
"qhxepbshlrhoecdaodgpousbzfcqjxulatciapuftffahhlmxbufgjuxstfjvljybfxnenlacmjqoymvamphpxnolwijwcecgwbcjhgdybfffwoygikvoecdggplfohemfypxfsvdrseyhmvkoovxhdvoavsqqbrsqrkqhbtmgwaurgisloqjixfwfvwtszcxwktkwesaxsmhsvlitegrlzkvfqoiiwxbzskzoewbkxtphapavbyvhzvgrrfriddnsrftfowhdanvhjvurhljmpxvpddxmzfgwwpkjrfgqptrmumoemhfpojnxzwlrxkcafvbhlwrapubhveattfifsmiounhqusvhywnxhwrgamgnesxmzliyzisqrwvkiyderyotxhwspqrrkeczjysfujvovsfcfouykcqyjoobfdgnlswfzjmyucaxuaslzwfnetekymrwbvponiaojdqnbmboldvvitamntwnyaeppjaohwkrisrlrgwcjqqgxeqerjrbapfzurcwxhcwzugcgnirkkrxdthtbmdqgvqxilllrsbwjhwqszrjtzyetwubdrlyakzxcveufvhqugyawvkivwonvmrgnchkzdysngqdibhkyboyftxcvvjoggecjsajbuqkjjxfvynrjsnvtfvgpgveycxidhhfauvjovmnbqgoxsafknluyimkczykwdgvqwlvvgdmufxdypwnajkncoynqticfetcdafvtqszuwfmrdggifokwmkgzuxnhncmnsstffqpqbplypapctctfhqpihavligbrutxmmygiyaklqtakdidvnvrjfteazeqmbgklrgrorudayokxptswwkcircwuhcavhdparjfkjypkyxhbgwxbkvpvrtzjaetahmxevmkhdfyidhrdeejapfbafwmdqjqszwnwzgclitdhlnkaiyldwkwwzvhyorgbysyjbxsspnjdewjxbhpsvj"
Thanks for any suggestion~~~~
One approach would be to sort the strings, then find where the first difference between them is:
var str = "1122334477422";
var str2 = "15122332247744";
var sorted = str.split('').sort();
var sorted2 = str2.split('').sort()
for(var i = 0; i< sorted2.length; i++){
if(i == sorted2.length || sorted[i] != sorted2[i]){
alert(sorted2[i]);
break;
}
}
You could use a hash table for counting. Add for every letter in the original string one and subtract one for every letter in the manipulated string. Then take only the hashes with nonempty count.
var data1 = "ymbgaraibkfmvocpizdydugvalagaivdbfsfbepeyccqfepzvtpyxtbadkhmwmoswrcxnargtlswqemafandgkmydtimuzvjwxvlfwlhvkrgcsithaqlcvrihrwqkpjdhgfgreqoxzfvhjzojhghfwbvpfzectwwhexthbsndovxejsntmjihchaotbgcysfdaojkjldprwyrnischrgmtvjcorypvopfmegizfkvudubnejzfqffvgdoxohuinkyygbdzmshvyqyhsozwvlhevfepdvafgkqpkmcsikfyxczcovrmwqxxbnhfzcjjcpgzjjfateajnnvlbwhyppdleahgaypxidkpwmfqwqyofwdqgxhjaxvyrzupfwesmxbjszolgwqvfiozofncbohduqgiswuiyddmwlwubetyaummenkdfptjczxemryuotrrymrfdxtrebpbjtpnuhsbnovhectpjhfhahbqrfbyxggobsweefcwxpqsspyssrmdhuelkkvyjxswjwofngpwfxvknkjviiavorwyfzlnktmfwxkvwkrwdcxjfzikdyswsuxegmhtnxjraqrdchaauazfhtklxsksbhwgjphgbasfnlwqwukprgvihntsyymdrfovaszjywuqygpvjtvlsvvqbvzsmgweiayhlubnbsitvfxawhfmfiatxvqrcwjshvovxknnxnyyfexqycrlyksderlqarqhkxyaqwlwoqcribumrqjtelhwdvaiysgjlvksrfvjlcaiwrirtkkxbwgicyhvakxgdjwnwmubkiazdjkfmotglclqndqjxethoutvjchjbkoasnnfbgrnycucfpeovruguzumgmgddqwjgdvaujhyqsqtoexmnfuluaqbxoofvotvfoiexbnprrxptchmlctzgqtkivsilwgwgvpidpvasurraqfkcmxhdapjrlrnkbklwkrvoaziznlpor",
data2 = "qhxepbshlrhoecdaodgpousbzfcqjxulatciapuftffahhlmxbufgjuxstfjvljybfxnenlacmjqoymvamphpxnolwijwcecgwbcjhgdybfffwoygikvoecdggplfohemfypxfsvdrseyhmvkoovxhdvoavsqqbrsqrkqhbtmgwaurgisloqjixfwfvwtszcxwktkwesaxsmhsvlitegrlzkvfqoiiwxbzskzoewbkxtphapavbyvhzvgrrfriddnsrftfowhdanvhjvurhljmpxvpddxmzfgwwpkjrfgqptrmumoemhfpojnxzwlrxkcafvbhlwrapubhveattfifsmiounhqusvhywnxhwrgamgnesxmzliyzisqrwvkiyderyotxhwspqrrkeczjysfujvovsfcfouykcqyjoobfdgnlswfzjmyucaxuaslzwfnetekymrwbvponiaojdqnbmboldvvitamntwnyaeppjaohwkrisrlrgwcjqqgxeqerjrbapfzurcwxhcwzugcgnirkkrxdthtbmdqgvqxilllrsbwjhwqszrjtzyetwubdrlyakzxcveufvhqugyawvkivwonvmrgnchkzdysngqdibhkyboyftxcvvjoggecjsajbuqkjjxfvynrjsnvtfvgpgveycxidhhfauvjovmnbqgoxsafknluyimkczykwdgvqwlvvgdmufxdypwnajkncoynqticfetcdafvtqszuwfmrdggifokwmkgzuxnhncmnsstffqpqbplypapctctfhqpihavligbrutxmmygiyaklqtakdidvnvrjfteazeqmbgklrgrorudayokxptswwkcircwuhcavhdparjfkjypkyxhbgwxbkvpvrtzjaetahmxevmkhdfyidhrdeejapfbafwmdqjqszwnwzgclitdhlnkaiyldwkwwzvhyorgbysyjbxsspnjdewjxbhpsvj",
hash = Object.create(null),
count = function (a) { hash[a] = (hash[a] || 0) + this; },
result;
data1.split('').forEach(count, 1);
data2.split('').forEach(count, -1);
result = Object.keys(hash).filter(function (k) { return hash[k]; });
console.log(result);
Just wondering if there is some other way than this.
var hashStringArray = function(array) {
array.sort();
return array.join('|');
};
I don't like sorting much and using that delimiter is not safe either if it's contained in one of the strings. In overall I need to produce same hash no matter the order of strings. It will be rather short arrays (up to 10 items), but it will be required very often so it shouldn't be too slow.
I intend to use it with ES6 Map object and I need to easily find same array collection.
Updated example of use
var theMap = new Map();
var lookup = function(arr) {
var item = null;
var hashed = hashStringArray(arr);
if (item = theMap.get( hashed )) {
return item;
}
theMap.set( hashed, itemBasedOnInput );
return itemBasedOnInput;
}
var arr1 = ['alpha','beta','gama'];
var arr2 = ['beta','alpha','gama'];
lookup(arr1) === lookup(arr2)
Performance tests
http://jsperf.com/hashing-array-of-strings/5
Two things occurred to me as the basis of a solution:
summing doesn't depend on order, which is actually a flaw in simple checksums (they don't catch changes in block order within a word), and
we can convert strings to summable numbers using their charcodes
Here's a function to do (2) :
charsum = function(s) {
var i, sum = 0;
for (i = 0; i < s.length; i++) {
sum += (s.charCodeAt(i) * (i+1));
}
return sum
}
Here's a version of (1) that computes an array hash by summing the charsum values:
array_hash = function(a) {
var i, sum = 0
for (i = 0; i < a.length; i++) {
var cs = charsum(a[i])
sum = sum + (65027 / cs)
}
return ("" + sum).slice(0,16)
}
Fiddle here: http://jsfiddle.net/WS9dC/11/
If we did a straight sum of the charsum values, then the array ["a", "d"] would have the same hash as the array ["b", "c"] - leading to undesired collisions. So based on using non-UTF strings, where charcodes go up to 255, and allowing for 255 characters in each string, then the max return value of charsum is 255 * 255 = 65025. So I picked the next prime number up, 65027, and used (65027 / cs) to compute the hash. I am not 100% convinced this removes collisions... perhaps more thought needed... but it certainly fixes the [a, d] versus [b, c] case.
Testing:
var arr1 = ['alpha','beta','gama'];
var arr2 = ['beta','alpha','gama'];
console.log(array_hash(arr1))
console.log(array_hash(arr2))
console.log(array_hash(arr1) == array_hash(arr2))
Outputs:
443.5322979371356
443.5322979371356
true
And testing a case that shows different hashes:
var arr3 = ['a', 'd'];
var arr4 = ['b', 'c'];
console.log(array_hash(arr3))
console.log(array_hash(arr4))
console.log(array_hash(arr3) == array_hash(arr4))
outputs:
1320.651443298969
1320.3792001649144
false
Edit:
Here's a revised version, which ignore duplicates from the arrays as it goes, and return the hash based on unique items only:
http://jsfiddle.net/WS9dC/7/
array_hash = function(a) {
var i, sum = 0, product = 1
for (i = 0; i < a.length; i++) {
var cs = charsum(a[i])
if (product % cs > 0) {
product = product * cs
sum = sum + (65027 / cs)
}
}
return ("" + sum).slice(0, 16)
}
testing:
var arr1 = ['alpha', 'beta', 'gama', 'delta', 'theta', 'alpha', 'gama'];
var arr2 = ["beta", "gama", "alpha", "theta", "delta", "beta"];
console.log(array_hash(arr1))
console.log(array_hash(arr2))
console.log(array_hash(arr1) === array_hash(arr2))
returns:
689.878503111701
689.878503111701
true
Edit
I've revised the answer above to account for arrays of words that have the same letters. We need these to return different hashes, which they now do:
var arr1 = ['alpha', 'beta']
var arr2 = ['alhpa', 'ateb']
The fix was to add a multiplier to the charsum func based on the char index:
sum += (s.charCodeAt(i) * (i+1));
If you calculate a numeric hash code for each string, then you can combine them with an operator where the order doesn't matter, like the ^ XOR operator, then you don't need to sort the array:
function hashStringArray(array) {
var code = 0;
for (var i = 0; i < array.length; i++) {
var n = 0;
for (var j = 0; j < array[i].length; j++) {
n = n * 251 ^ array[i].charCodeAt(j);
}
code ^= n;
}
return code
};
You can do this:
var hashStringArray = function(array) {
return array.sort().join('\u200b');
};
The \u200b character is an unicode character that also means null, but is not the same as the \0 character, which is most widely used.
'\u200b' == '\0'
> false
An idea to have very fast hash if your set of possible string is less than 32 items long : hash the string with a built-in hash function that will return power-of two as hash :
function getStringHash(aString) {
var currentPO2 = 0;
var hashSet = [];
getStringHash = function ( aString) {
var aHash = hashSet[aString];
if (aHash) return aHash;
aHash = 1 << currentPO2++;
hashSet[aString] = aHash;
return aHash;
}
return getStringHash(aString);
}
Then use this hash on your string array, ORing the hashes ( | ) :
function getStringArrayHash( aStringArray) {
var aHash = 0;
for (var i=0; i<aStringArray.length; i++) {
aHash |= getStringHash(aStringArray[i]);
}
return aHash;
}
So to test a bit :
console.log(getStringHash('alpha')); // 1
console.log(getStringHash('beta')); // 2
console.log(getStringHash('gamma')); // 4
console.log(getStringHash('alpha')); // 1 again
var arr1 = ['alpha','beta','gama'];
var arr2 = ['beta','alpha','gama'];
var arr3 = ['alpha', 'teta'];
console.log(getStringArrayHash(arr1)); // 11
console.log(getStringArrayHash(arr2)); // 11 also, like for arr1
var arr3 = ['alpha', 'teta'];
console.log(getStringArrayHash(arr3)); // 17 : a different array has != hashset
jsbin is here : http://jsbin.com/rozanufa/1/edit?js,console
RQ !!! with this method, arrays are considered as set, meaning that a repeated item won't change the hash of an array !!!
This HAS to be faster since it uses only 1) function call 2) lookup 3) integer arithmetic.
So no sort, no (long) string, no concat.
jsperf confirms that :
http://jsperf.com/hashing-array-of-strings/4
EDIT :
version with prime numbers, here : http://jsbin.com/rozanufa/3/edit?js,console
// return the unique prime associated with the string.
function getPrimeStringHash(aString) {
var hashSet = [];
var currentPrimeIndex = 0;
var primes = [ 2, 3, 5, 7, 11, 13, 17 ];
getPrimeStringHash = function ( aString) {
var aPrime = hashSet[aString];
if (aPrime) return aPrime;
if (currentPrimeIndex == primes.length) aPrime = getNextPrime();
else aPrime = primes[currentPrimeIndex];
currentPrimeIndex++
hashSet[aString] = aPrime;
return aPrime;
};
return getPrimeStringHash(aString);
// compute next prime number, store it and returns it.
function getNextPrime() {
var pr = primes[primes.length-1];
do {
pr+=2;
var divides = false;
// discard the number if it divides by one earlier prime.
for (var i=0; i<primes.length; i++) {
if ( ( pr % primes[i] ) == 0 ) {
divides = true;
break;
}
}
} while (divides == true)
primes.push(pr);
return pr;
}
}
function getStringPrimeArrayHash( aStringArray) {
var primeMul = 1;
for (var i=0; i<aStringArray.length; i++) {
primeMul *= getPrimeStringHash(aStringArray[i]);
}
return primeMul;
}
function compareByPrimeHash( aStringArray, anotherStringArray) {
var mul1 = getStringPrimeArrayHash ( aStringArray ) ;
var mul2 = getStringPrimeArrayHash ( anotherStringArray ) ;
return ( mul1 > mul2 ) ?
! ( mul1 % mul2 )
: ! ( mul2 % mul1 );
// Rq : just test for mul1 == mul2 if you are sure there's no duplicates
}
Tests :
console.log(getPrimeStringHash('alpha')); // 2
console.log(getPrimeStringHash('beta')); // 3
console.log(getPrimeStringHash('gamma')); // 5
console.log(getPrimeStringHash('alpha')); // 2 again
console.log(getPrimeStringHash('a1')); // 7
console.log(getPrimeStringHash('a2')); // 11
var arr1 = ['alpha','beta','gamma'];
var arr2 = ['beta','alpha','gamma'];
var arr3 = ['alpha', 'teta'];
var arr4 = ['alpha','beta','gamma', 'alpha']; // == arr1 + duplicate 'alpha'
console.log(getStringPrimeArrayHash(arr1)); // 30
console.log(getStringPrimeArrayHash(arr2)); // 30 also, like for arr1
var arr3 = ['alpha', 'teta'];
console.log(getStringPrimeArrayHash(arr3)); // 26 : a different array has != hashset
console.log(compareByPrimeHash(arr1, arr2) ); // true
console.log(compareByPrimeHash(arr1, arr3) ); // false
console.log(compareByPrimeHash(arr1, arr4) ); // true despite 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)
I have this input sample:
var c1 = "s_A_3";
var c2 = "s_B_10";
var c3 = "s_B_9";
var c4 = "s_C_18";
var c5 = "s_C_19";
var c6 = "s_C_20";
Which can easily be concatenated to:
var keypairs = ["A_3","B_10","B_9","C_18","C_19","C_20"];
And I want to convert this to a multidimensional array like this:
var groupArray = [["A",[3]],["B",[10,9]],["C",[18,19,20]]];
It's like a kind of card-sorting. How can I achieve this?
Maybe something like this:
function makeGroups(arr) {
var result = [], prev;
for(var i = 0; i < arr.length; i++) {
var x = arr[i].split("_");
if (prev !== x[0]) {
prev = x[0];
result.push([prev, []]);
}
result[result.length - 1][1].push(x[1]); // or .push(parseInt(x[1], 10))
}
return result;
}
var keypairs = ["A_3","B_10","B_9","C_18","C_19","C_20"];
console.log(makeGroups(keypairs));
// [["A",["3"]],["B",["10","9"]],["C",["18","19","20"]]]
Demonstration
The above method assumes the groups will be contiguous (e.g. all B_ elements appear together). In case your input may be out of order, you can tweak this algorithm to still group all elements together regardless of where they appear in the input:
function makeGroups(arr) {
var result = [], keys = {};
for(var i = 0; i < arr.length; i++) {
var x = arr[i].split("_");
if (!(x[0] in keys)) {
keys[x[0]] = [];
result.push([x[0], keys[x[0]]]);
}
keys[x[0]].push(x[1]); // or .push(parseInt(x[1], 10))
}
return result;
}
var keypairs = ["A_3","B_10","C_18","C_19","C_20","B_9"];
console.log(makeGroups(keypairs));
// [["A",["3"]],["B",["10","9"]],["C",["18","19","20"]]]
Demonstration
When you need to mention "key value pairs" in a JS program, it's usually most appropriate to use... key value pairs =D.
function solution(input) {
var kvp = {},
result = [];
input.forEach(function (el) {
var cut = el.split("_"),
alpha = cut[0],
numeric = cut[1],
elsWithSameAlpha = kvp[alpha] = kvp[alpha] || [];
elsWithSameAlpha.push(numeric);
});
Object.keys(kvp).forEach(function (key) {
result.push([key, kvp[key]]);
});
return result;
}
JSFiddle: http://jsfiddle.net/3WdzL/1/
I need to convert locale JS object files to flattened versions and back again:
Orig locale object:
var localeObj = {
toolbar: {
link: {
back: 'Back',
menu: 'Menu',
},
flatTest: 'something'
},
countries: [
["AF", "Afghanistan"],
["AX", "Åland Islands"],
['nested', [1, 2, 3, 4]],
["AL", "Albania"]
]
};
Using the following function:
function flattenObj(obj) {
var flattenedObj = {};
var walk = function(obj, stringMap) {
for(k in obj) {
var computedKey = stringMap + (stringMap ? '.' + k : k);
if(typeof obj[k] !== 'object') {
flattenedObj[computedKey] = obj[k];
} else {
walk(obj[k], computedKey);
}
}
};
walk(obj, '');
return flattenedObj;
}
Would produce a flattened object:
{
toolbar.link.back: Back
toolbar.link.menu: Menu
toolbar.flatTest: something
countries.0.0: AF
countries.0.1: Afghanistan
countries.1.0: AX
countries.1.1: Åland Islands
countries.2.0: nested
countries.2.1.0: 1
countries.2.1.1: 2
countries.2.1.2: 3
countries.2.1.3: 4
countries.3.0: AL
countries.3.1: Albania
}
Converting back with the following func works fine for objects:
function deepenObj(obj) {
var deepenedObj = {}, tmp, parts, part;
for (var k in obj) {
tmp = deepenedObj;
parts = k.split('.');
var computedKey = parts.pop();
while (parts.length) {
part = parts.shift();
tmp = tmp[part] = tmp[part] || {};
}
tmp[computedKey] = obj[k];
}
return deepenedObj;
}
But produces a structure like this for the arrays:
region: {
country: {
0: {
0: 'AF',
1: 'Afghanistan'
},
...
2: {
0: 'nested',
1: {
0: 1,
1: 2,
3: 4,
4: 5
}
}
}
}
Obviously this isn't the desired results for the arrays and I haven't been able to come up with a safe, elegant or even working solution yet. PS I am happy to save the arrays to strings differently if it makes converting back easier. Thanks!
You should either keep track if an object is actually an array:
var walk = function(obj, stringMap) {
if (Array.isArray(obj) {
for (var k = 0; k < obj.length; k++)
var computedKey = stringMap ? stringMap + ',' + k : k;
} else {
for (var k in obj) {
var computedKey = stringMap ? stringMap + '.' + k : k;
...
Then, when deepening:
for (var k in obj) {
tmp = deepenedObj;
parts = ["."].concat(k.split(/([\.,])/));
var computedKey = parts.pop(), sign;
while (parts.length) {
sign = parts.shift();
part = !parts.length ? computedKey : parts.shift();
tmp = tmp[part] = tmp[part] || (sign === "," ? [] : {});
}
tmp[computedKey] = obj[k];
}
Note that Array.isArray could be undefined. You can use obj instanceof Array instead.
This solution works if localeObj is an object literal and not an array, because the first point/comma isn't saved in the computed key. You can modify the function if you need to.
The trick here is to use an unusual behaviour of split that pushes captured groups in the splitted array when used with regular expressions, so before every key part there's the proper separator.
Use JSON.stringify() and JSON.parse():
var flattenedObj = JSON.stringify(localeObj);
vat deepenedObj = JSON.parse(flattenedObj);
Demo