How can I compare adjecent string elements in an array - javascript? - javascript

Given an array of strings, I'd like to check for opposite directions and cancel them out (remove them from the array) and return a new array.
For example:
let arr = ["N", "E", "W", "S", "W", "S", "N", "W"]
should reduce to:
newArr = ["W", "W"]
Here is what I have come up with so far. I am getting undefined and I am not sure why.
let arr = ["N", "E", "W", "S", "W", "S", "N", "W"]
function checkForOpposites(arr) {
let nOpp = ["N", "S"]
let sOpp = ["S", "N"]
let eOpp = ["E", "W"]
let wOpp = ["W", "E"]
for (var i = 0; i < arr.length; i++) {
if (arr[i] + arr[i + 1] === nOpp)
if (arr[i] + arr[i + 1] === sOpp)
if (arr[i] + arr[i + 1] === eOpp)
if (arr[i] + arr[i + 1] === wOpp) {
return true
}
}
}
function newDirections(arr) {
arr.filter(checkForOpposites)
}

You could filter the array until the length is not changing.
let
array = ["N", "E", "W", "S", "W", "S", "N", "W"],
temp;
do {
let index = -1;
temp = array;
array = temp.filter((v, i, { [i + 1]: next }) => {
if (i === index) return false;
if (
v === 'N' && next === 'S' || v === 'S' && next === 'N' ||
v === 'E' && next === 'W' || v === 'W' && next === 'E'
) {
index = i + 1;
return false;
}
return true;
});
} while (temp.length !== array.length)
console.log(array);

function cancelDirections(array) {
var ncnt = 0, ecnt = 0, scnt = 0, wcnt = 0;
var output = "";
for (var n of array) {
switch (n) {
case "N":
ncnt++;
break;
case "E":
ecnt++;
break;
case "S":
scnt++;
break;
case "W":
wcnt++;
break;
}
}
if (ecnt > wcnt) { ecnt -= wcnt; wcnt = 0;}
else { wcnt -= ecnt; ecnt = 0}
if (ncnt > scnt) { ncnt -= scnt; scnt = 0; }
else { scnt -= ncnt; ncnt = 0; }
return output + "N".repeat(ncnt) + "S".repeat(scnt) + "E".repeat(ecnt) + "W".repeat(wcnt);
}
let arr = ["N", "E", "W", "S", "W", "S", "N", "W"]
console.log(cancelDirections(arr))
Explanation:
I keep track of the count of each element, and then just cancel them out. This approach is O(n); I would be interested if there was a faster way.

First issue is you get undefined because you are not returning anything
function newDirections(arr) { arr.filter(checkForOpposites) } // <-- no return so undefined
Second issue is your logic is wrong and your code is basically saying everything would have to be equal which is not possible. Your code looks like this:
if (arr[i] + arr[i + 1] === nOpp) {
if (arr[i] + arr[i + 1] === sOpp) {
if (arr[i] + arr[i + 1] === eOpp) {
if (arr[i] + arr[i + 1] === wOpp) {
return true;
}
}
}
}
Next you are using filter which loops multiple times and then you are looping inside of it. Filter is not the way to go. You want to do something with a loop.
var opposites = {
N: 'S',
S: 'N',
E: 'W',
W: 'E',
};
function newDirections(orgArr) {
// in case you want original cloning array
var arr = orgArr.slice();
// loop from start to 2nd to last element in array
var i = 0;
while (i < arr.length - 1) {
// grab current and next direction
var dir = arr[i];
var nextDir = arr[i + 1];
// check to see if they are opposite
if (opposites[dir] === nextDir) {
// remove them
arr.splice(i, 2); // index and 2 items
// if we are not at the start move back one position so we can check the new direction
if (i > 0) {
i--;
}
} else {
// if not opposite move forward
i++;
}
}
return arr;
}
const arr = ["N", "E", "W", "S", "W", "S", "N", "W"];
console.log(newDirections(arr));
console.log(newDirections(["N", "S", "E", "W", "W", "S"]));
console.log(newDirections(["E", "N", "S", "E", "W", "W", "S"]));
console.log(newDirections(["E", "E", "W", "N", "S", "E", "W", "W"]));

You can map the directions to vectors, sum the vectors and reduce the resulting vector to a list of directions.
var arr = ["N", "E", "W", "S", "W", "S", "N", "W"]
var vecMap = {
N: [1,0],
E: [0,1],
S: [-1,0],
W: [0,-1]
};
var revMap = [
['N','S'],
['E','W']
];
const dir2Vec = dir => vecMap[dir];
const vec2Dir = (acc, cur, i) => {
//get the direction based on the reverse map.
const dir = revMap[i][+(cur < 0)];
//array with n items where n is the length of the vector
const dup = Array.from({length: Math.abs(cur)}).fill(dir);
return [...acc, ...dup]
}
const sumVec = (a,b) => [a[0]+b[0], a[1]+b[1]];
const res = arr
.map(dir2Vec)
.reduce(sumVec)
.reduce(vec2Dir, [])
console.log(res)

First of all, you cannot compare arrays like this if (arr[i] + arr[i + 1] === nOpp). In this case, === will compare if both sides of the comparison are Array type objects, and if they are the same object, not if their content is the same, for that you should use other ways.
Open node.js CLI and paste this code
const nOpp = ["N", "S"];
console.log(nOpp === ["N", "S"]);
and you'll see false printed in the console.
For a more detailed explanation, look here: How to compare arrays in JavaScript?
Second, nesting if like this is equivalent to successive ANDs
if (arr[i] + arr[i + 1] === nOpp)
if (arr[i] + arr[i + 1] === sOpp)
if (arr[i] + arr[i + 1] === eOpp)
if (arr[i] + arr[i + 1] === wOpp) {
...
}
This means that only if the first if evaluates to true the second one will be evaluated if this evaluates to true the third will be evaluated, and so on.
Now, that all being said, here is my version.
I just took into account the first paragraph of your question, and the desired result, then came to this:
let arr = ["N", "E", "W", "S", "W", "S", "N", "W"]
const opposites = [
["N", "S"],
["W", "E"],
];
const checkForOpposites = data => {
const result = [];
data.forEach((v0, i, a) => {
let oppositeFound = false;
opposites.forEach(o => {
if (v0 != a[i+1] && o.includes(v0) && o.includes(a[i+1])) {
oppositeFound = true
result.push(i, i+1);
}
});
// console.log(`${i} => ${v0} | ${i+1} => ${a[i+1]} | ${oppositeFound}`);
});
return result;
}
const cleanOpposites = data => {
let result = data;
let n = [];
do {
n = checkForOpposites(result);
// console.log(n);
if (n.length > 0) {
result = result.reduce((accum, curr, i) => {
if (!n.includes(i)) {
accum.push(curr);
}
return accum;
}, []);
}
// console.log(result);
} while (n.length > 0);
return result;
}
const r0 = cleanOpposites(arr);
console.log(arr);
console.log(r0);
The cleaning process should be recursive due to this:
In the first iteraction we get this result:
indexes of items deleted: [1, 2, 5, 6]
resulting array: ["N", "S", "W", "W"]
But there remains ["N", "S",... that shouldn't be part of the final result, so to achieve your requirement, I think we must iterate through the checkForOpposites function till it returns no matches.
Certainly, the code can be shortened, but this would give you an idea of how to do it.

Related

Loop Through String of Words, Return Word With Highest Score According To Character Value in Object - JavaScript

I'm trying to figure out how to solve this kata on CodeWars.
Function high recieves a string and returns the word with the highest "score" according to which letters in the word are present. The letters receive a score based on their position in the alphabet. So a = 1 point, b = 2 points, c = 3 points, and so on.
I think it makes sense to create an object where all of the letters in the alphabet are assigned a value:
If the letter in the word appears in alphabetScore, that word will receive its "points" and continue on to the next letter in the word, increasing the total points of the word.
I have:
function high(string) {
let words = string.split(" ");
let wordScore = 0;
const alphabetScore = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5,
f: 6,
g: 7,
h: 8,
i: 9,
j: 10,
k: 11,
l: 12,
m: 13,
n: 14,
o: 15,
p: 16,
q: 17,
r: 18,
s: 19,
t: 20,
u: 21,
v: 22,
w: 23,
x: 24,
y: 25,
z: 26
}
let word = words[i];
let wordCount = 0;
//loop through all words in the string
for (let i = 0; i < words.length; i++) {
let word = words[i];
//loop through all characters in each word
for (let j = 0; j < word.length; j++) {
let value = alphabetScore[j];
wordCount += alphabetScore[value];
}
}
return wordCount;
}
console.log(high("man i need a taxi up to ubud"));
And this is returning an error saying
i is not defined
in let word = words[i] - how else would I define a word, then?
If it's possible to solve this Kata with my existing logic (using for-loops), please do so.
EDIT: Changed wordCount = alphabetScore.value++; to wordCount += alphabetScore[value];
EDIT 2: This is now returning NaN
EDIT 3: Latest attempt:
function myScore(input) {
let key = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
"w", "x", "y", "z"
];
let bestWord = "";
let bestScore = 0;
let words = input.split(" ");
for (let i = 0; i < words.length; i++) {
let score = 0;
let word = words[i];
for (let j = 0; j < word.length; j++) {
let char = word[j];
score += (key.indexOf(char) + 1);
}
if (score > bestScore) {
bestScore = score;
bestWord = word;
}
}
return bestWord;
}
ReferenceError: high is not defined
at Test.describe._
ran successfully on codewars
let key = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
"w", "x", "y", "z"
];
function wordScore(word) {
let score = 0;
for (let j = 0; j < word.length; j++) {
let char = word[j];
score += (key.indexOf(char) + 1);
}
return score;
}
function high(x) {
let bestWord = "";
let bestScore = 0;
words = x.split(" ");
for (let i = 0; i < words.length; i++) {
let word = words[i];
let score = wordScore(word);
if (score > bestScore) {
bestScore = score;
bestWord = word;
}
}
return bestWord;
}
console.log(high("man i need a taxi up to ubud"));
NaN means "not a number." It's usually a sign that you've tried to do arithmetic on a null or something at some point.
In this instance, your alphabetScore is a hashmap of letters - but you're looking for numeric keys let value = alphabetScore[j];: that will return undefined and undefined + 0 == NaN. Instead you need to say let value = alphabetScore[word[j]] - get the letter of the word, not the index.
To do as Jonas suggested, make two new variables where your first let word = words[i] is - and get rid of that - let highScore = 0; let highScoreWord = ""; to hold the highest one you find. Move the let wordCount = 0; inside the loop as well. Now, for each word, you'll get the word, and reset the count.
Finally, after the inner loop, say, if (wordCount > highScore) { highScore = wordCount; highScoreWord = word; }. So if it's higher than your current highest, save it; otherwise just ignore it.
Then return highScoreWord and you should be golden!

javascript compare a number to object keys and get the value of the smallest

Say we have:
const list = {
1: "a",
10: "b",
20: "c",
30: "d",
40: "e"
};
const value = 15;
how can I efficiently compare the value against the object keys and get the value of the corresponding smaller range? and in this example, the expected answer is b because 15 is between 10 and 20.
i wanted to go this way:
for(var i=0; i < keys.length; i++){
const item = parseInt(keys[i],10);
if (item == keys[i]) {
return keys[i];
}
}
but it is not efficient...
You can use Object.keys which will get all object keys in new array, and in .some(as suggested #Keith) stop the loop (return true), when current value is getting bigger then value, your result will be stored in previous var
const list = {
1: "a",
10: "b",
20: "c",
30: "d",
40: "e"
};
const value = 15;
let previous = 0;
Object.keys(list).some(k => {
if (k > value) {
return true;
}
previous = k
});
console.log(list[previous]);
You can use Array.findIndex() to find the 1st index which is higher than the value. If none is found (return value is -1) take the element at the last key, else take the value at keys[index - 1. If the value is less than the 1st key it will return undefined.
const findLowerClosest = (value, arr) => {
const keys = Object.keys(list);
const index = keys.findIndex(key => +key > value);
const atKey = index !== -1 ? index - 1 : keys.length - 1;
return arr[keys[atKey]];
}
const list = { 1: "a", 10: "b", 20: "c", 30: "d", 40: "e" };
console.log(findLowerClosest(15, list)); // b
console.log(findLowerClosest(75, list)); // e
console.log(findLowerClosest(-3, list)); // undefined
If the keys are pre-sorted as in your example, use a binary search in log(n) time complexity. The catch is that dumping the keys into an array is a one-time linear step (and a one-time sort would be O(n log n) if necessary), so this is worth considering if you're planning on running multiple searches on one structure.
const bisect = (a, target, lo=0, hi=a.length-1) => {
while (lo <= hi) {
const mid = ~~((hi - lo) / 2 + lo);
if (a[mid] === target) {
return a[mid];
}
else if (a[mid] < target) {
lo = mid + 1;
}
else {
hi = mid - 1;
}
}
return a[~~((hi-lo)/2+lo)];
};
const list = {
1: "a",
10: "b",
20: "c",
30: "d",
40: "e"
};
const keys = Object.keys(list);
console.log(list[bisect(keys, 15)]);
console.log(list[bisect(keys, 16)]);
console.log(list[bisect(keys, -50)]);
console.log(list[bisect(keys, 50)]);
You can use filter to get the values which are less than 15, then do Math.max, then use this as key to retrieve value from the object
const list = {
1: "a",
10: "b",
20: "c",
30: "d",
40: "e"
};
const value = 15;
function getVal(n) {
let getKeys = Math.max.apply(null, Object.keys(list).filter((item) => {
return item < n
}));
return (list[getKeys])
}
console.log(getVal(value))
You can also sort the keys and then use reduce to find the lower key of the target range:
const list = {
1: "a",
10: "b",
20: "c",
30: "d",
40: "e"
};
const value = 15;
const answer = list[Object.keys(list)
.map(a => parseInt(a))
.sort((a, b) => a - b)
.reduce((acc, curr) => (acc < value && value < curr) ? acc : curr)];
console.log(answer);

Checking for duplicate strings in JavaScript array

I have JS array with strings, for example:
var strArray = [ "q", "w", "w", "e", "i", "u", "r"];
I need to compare for duplicate strings inside array, and if duplicate string exists, there should be alert box pointing to that string.
I was trying to compare it with for loop, but I don't know how to write code so that array checks its own strings for duplicates, without already pre-determined string to compare.
The findDuplicates function (below) compares index of all items in array with index of first occurrence of same item. If indexes are not same returns it as duplicate.
let strArray = [ "q", "w", "w", "w", "e", "i", "u", "r"];
let findDuplicates = arr => arr.filter((item, index) => arr.indexOf(item) != index)
console.log(findDuplicates(strArray)) // All duplicates
console.log([...new Set(findDuplicates(strArray))]) // Unique duplicates
Using ES6 features
Because each value in the Set has to be unique, the value equality will be checked.
function checkIfDuplicateExists(arr) {
return new Set(arr).size !== arr.length
}
var arr = ["a", "a", "b", "c"];
var arr1 = ["a", "b", "c"];
console.log(checkIfDuplicateExists(arr)); // true
console.log(checkIfDuplicateExists(arr1)); // false
var strArray = [ "q", "w", "w", "e", "i", "u", "r", "q"];
var alreadySeen = {};
strArray.forEach(function(str) {
if (alreadySeen[str])
console.log(str);
else
alreadySeen[str] = true;
});
I added another duplicate in there from your original just to show it would find a non-consecutive duplicate.
Updated version with arrow function:
const strArray = [ "q", "w", "w", "e", "i", "u", "r", "q"];
const alreadySeen = {};
strArray.forEach(str => alreadySeen[str] ? console.log(str) : alreadySeen[str] = true);
You could take a Set and filter to the values that have already been seen.
var array = ["q", "w", "w", "e", "i", "u", "r"],
seen = array.filter((s => v => s.has(v) || !s.add(v))(new Set));
console.log(seen);
Using some function on arrays:
If any item in the array has an index number from the beginning is not equals to index number from the end, then this item exists in the array more than once.
// vanilla js
function hasDuplicates(arr) {
return arr.some( function(item) {
return arr.indexOf(item) !== arr.lastIndexOf(item);
});
}
function hasDuplicates(arr) {
var counts = [];
for (var i = 0; i <= arr.length; i++) {
if (counts[arr[i]] === undefined) {
counts[arr[i]] = 1;
} else {
return true;
}
}
return false;
}
// [...]
var arr = [1, 1, 2, 3, 4];
if (hasDuplicates(arr)) {
alert('Error: you have duplicates values !')
}
Simple Javascript (if you don't know ES6)
function hasDuplicates(arr) {
var counts = [];
for (var i = 0; i <= arr.length; i++) {
if (counts[arr[i]] === undefined) {
counts[arr[i]] = 1;
} else {
return true;
}
}
return false;
}
// [...]
var arr = [1, 1, 2, 3, 4];
if (hasDuplicates(arr)) {
alert('Error: you have duplicates values !')
}
function hasDuplicateString(strings: string[]): boolean {
const table: { [key: string]: boolean} = {}
for (let string of strings) {
if (string in table) return true;
table[string] = true;
}
return false
}
Here the in operator is generally considered to be an 0(1) time lookup, since it's a hash table lookup.
var elems = ['f', 'a','b','f', 'c','d','e','f','c'];
elems.sort();
elems.forEach(function (value, index, arr){
let first_index = arr.indexOf(value);
let last_index = arr.lastIndexOf(value);
if(first_index !== last_index){
console.log('Duplicate item in array ' + value);
}else{
console.log('unique items in array ' + value);
}
});
You have to create an empty array then check each element of the given array if the new array already has the element it will alert you.
Something like this.
var strArray = [ "q", "w", "w", "e", "i", "u", "r"];
let newArray =[];
function check(arr){
for(let elements of arr){
if(newArray.includes(elements)){
alert(elements)
}
else{
newArray.push(elements);
}
}
return newArray.sort();
}
check(strArray);
Use object keys for good performance when you work with a big array (in that case, loop for each element and loop again to check duplicate will be very slowly).
var strArray = ["q", "w", "w", "e", "i", "u", "r"];
var counting = {};
strArray.forEach(function (str) {
counting[str] = (counting[str] || 0) + 1;
});
if (Object.keys(counting).length !== strArray.length) {
console.log("Has duplicates");
var str;
for (str in counting) {
if (counting.hasOwnProperty(str)) {
if (counting[str] > 1) {
console.log(str + " appears " + counting[str] + " times");
}
}
}
}
This is the simplest solution I guess :
function diffArray(arr1, arr2) {
return arr1
.concat(arr2)
.filter(item => !arr1.includes(item) || !arr2.includes(item));
}
const isDuplicate = (str) =>{
return new Set(str.split("")).size === str.length;
}
You could use reduce:
const arr = ["q", "w", "w", "e", "i", "u", "r"]
arr.reduce((acc, cur) => {
if(acc[cur]) {
acc.duplicates.push(cur)
} else {
acc[cur] = true //anything could go here
}
}, { duplicates: [] })
Result would look like this:
{ ...Non Duplicate Values, duplicates: ["w"] }
That way you can do whatever you want with the duplicate values!

manually sorting a paragraph by made-up alphabet with javascript

I'm trying to sort a paragraph alphabetically, not according to the normal ABC but a made-up one (var order).
I wrote this function and it works great, but only for the first letter of each word - not in-word sorting as well (for example, in correct ABC 'banana' would come before 'birthday').
I'm not sure where to go from here.
$("#send").click(function () {
var text = $("#text").val().replace(/[^A-Za-z0-9_\s]/g, "").toUpperCase().split(" ");
var order = ["Q", "B", "K", "D", "H", "V", "Z", "E", "F", "O", "G", "L", "M", "S", "N", "P", "I", "X", "A", "R", "W", "U", "C", "J", "T", "Y"];
var i, t, j;
var newText = []; // will hold the new alphabet
// function to sort the words:
for (i = 0; i < order.length; i++) {
for (t = 0; t < text.length; t++) {
var firstChar = text[t][0];
if (order[i] == firstChar) {
newText.push(text[t]);
}
}
}
console.log(newText.join(','));
});
EDIT:
An example input can be: "Hi dan don't you think that this is awesome",
and I want the output to be: "don't dan hi is awesome this think that you".
You could use an object with the index of the letters and use Array#sort with a callback which looks for every letter adn calculates the order.
function foo(text) {
var text = text.replace(/[^A-Za-z0-9_\s]/g, "").toUpperCase().split(" "),
order = "QBKDHVZEFOGLMSNPIXARWUCJTY",
ref = {};
order.split('').forEach(function (a, i) { ref[a] = i + 1; });
text.sort(function (a, b) {
var i = 0, v;
do {
v = (ref[a[i]] || 0) - (ref[b[i]] || 0);
i++;
} while (!v)
return v;
});
console.log(text.join(', '));
}
foo('a aa ab b ba bb');
foo('banana birthday');
The problem with your algorithm is that it only compares the first letter in each word, but if the letters are the same the algorithm needs to compare the next letter in each word. Here's a solution that uses recursion:
function doSort(inputArr) {
var words = inputArr.slice(0);
var alphabet = ["Q", "B", "K", "D", "H", "V", "Z", "E", "F", "O", "G", "L", "M", "S", "N", "P", "I", "X", "A", "R", "W", "U", "C", "J", "T", "Y"];
words.sort(function(item1, item2) {
return sortRecursive(item1, item2, 0);
});
function sortRecursive(item1, item2, idx) {
if (item1.length <= idx && item2.length <= idx) {
return 0;
} else if (item1.length <= idx) {
return -1;
} else if (item2.length <= idx) {
return 1;
} else if (item1[idx] == item2[idx]) {
return sortRecursive(item1, item2, idx+1);
} else {
return alphabet.indexOf(item1[idx].toUpperCase()) - alphabet.indexOf(item2[idx].toUpperCase());
}
}
return words;
}
var arr = ["banana", "quebec", "bird", "birthday", "birdman", "bird"];
var sortedArr = doSort(arr);
console.log('unsorted',arr);
console.log('sorted', sortedArr);
https://jsfiddle.net/2qgaaozo/

How to split a long array into smaller arrays, with JavaScript

I have an array of e-mails (it can be just 1 email, or 100 emails), and I need to send the array with an ajax request (that I know how to do), but I can only send an array that has 10 or less e-mails in it. So if there is an original array of 20 e-mails I will need to split them up into 2 arrays of 10 each. or if there are 15 e-mails in the original array, then 1 array of 10, and another array of 5. I'm using jQuery, what would be the best way to do this?
Don't use jquery...use plain javascript
var a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var b = a.splice(0,10);
//a is now [11,12,13,14,15];
//b is now [1,2,3,4,5,6,7,8,9,10];
You could loop this to get the behavior you want.
var a = YOUR_ARRAY;
while(a.length) {
console.log(a.splice(0,10));
}
This would give you 10 elements at a time...if you have say 15 elements, you would get 1-10, the 11-15 as you wanted.
var size = 10; var arrayOfArrays = [];
for (var i=0; i<bigarray.length; i+=size) {
arrayOfArrays.push(bigarray.slice(i,i+size));
}
console.log(arrayOfArrays);
Unlike splice(), slice() is non-destructive to the original array.
Just loop over the array, splicing it until it's all consumed.
var a = ['a','b','c','d','e','f','g']
, chunk
while (a.length > 0) {
chunk = a.splice(0,3)
console.log(chunk)
}
output
[ 'a', 'b', 'c' ]
[ 'd', 'e', 'f' ]
[ 'g' ]
You can use lodash:
https://lodash.com/docs
_.chunk(['a', 'b', 'c', 'd'], 2);
// → [['a', 'b'], ['c', 'd']]
Array.reduce could be inefficient for large arrays, especially with the mod operator. I think a cleaner (and possibly easier to read) functional solution would be this:
const chunkArray = (arr, size) =>
arr.length > size
? [arr.slice(0, size), ...chunkArray(arr.slice(size), size)]
: [arr];
Assuming you don't want to destroy the original array, you can use code like this to break up the long array into smaller arrays which you can then iterate over:
var longArray = []; // assume this has 100 or more email addresses in it
var shortArrays = [], i, len;
for (i = 0, len = longArray.length; i < len; i += 10) {
shortArrays.push(longArray.slice(i, i + 10));
}
// now you can iterate over shortArrays which is an
// array of arrays where each array has 10 or fewer
// of the original email addresses in it
for (i = 0, len = shortArrays.length; i < len; i++) {
// shortArrays[i] is an array of email addresss of 10 or less
}
Another implementation:
const arr = ["H", "o", "w", " ", "t", "o", " ", "s", "p", "l", "i", "t", " ", "a", " ", "l", "o", "n", "g", " ", "a", "r", "r", "a", "y", " ", "i", "n", "t", "o", " ", "s", "m", "a", "l", "l", "e", "r", " ", "a", "r", "r", "a", "y", "s", ",", " ", "w", "i", "t", "h", " ", "J", "a", "v", "a", "S", "c", "r", "i", "p", "t"];
const size = 3;
const res = arr.reduce((acc, curr, i) => {
if ( !(i % size) ) { // if index is 0 or can be divided by the `size`...
acc.push(arr.slice(i, i + size)); // ..push a chunk of the original array to the accumulator
}
return acc;
}, []);
// => [["H", "o", "w"], [" ", "t", "o"], [" ", "s", "p"], ["l", "i", "t"], [" ", "a", " "], ["l", "o", "n"], ["g", " ", "a"], ["r", "r", "a"], ["y", " ", "i"], ["n", "t", "o"], [" ", "s", "m"], ["a", "l", "l"], ["e", "r", " "], ["a", "r", "r"], ["a", "y", "s"], [",", " ", "w"], ["i", "t", "h"], [" ", "J", "a"], ["v", "a", "S"], ["c", "r", "i"], ["p", "t"]]
NB - This does not modify the original array.
Or, if you prefer a functional, 100% immutable (although there's really nothing bad in mutating in place like done above) and self-contained method:
function splitBy(size, list) {
return list.reduce((acc, curr, i, self) => {
if ( !(i % size) ) {
return [
...acc,
self.slice(i, i + size),
];
}
return acc;
}, []);
}
As a supplement to #jyore's answer, and in case you still want to keep the original array:
var originalArray = [1,2,3,4,5,6,7,8];
var splitArray = function (arr, size) {
var arr2 = arr.slice(0),
arrays = [];
while (arr2.length > 0) {
arrays.push(arr2.splice(0, size));
}
return arrays;
}
splitArray(originalArray, 2);
// originalArray is still = [1,2,3,4,5,6,7,8];
I would like to share my solution as well. It's a little bit more verbose but works as well.
var data = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var chunksize = 4;
var chunks = [];
data.forEach((item)=>{
if(!chunks.length || chunks[chunks.length-1].length == chunksize)
chunks.push([]);
chunks[chunks.length-1].push(item);
});
console.log(chunks);
Output (formatted):
[ [ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12],
[13, 14, 15 ] ]
You can start with an empty array and push inside it sections with your desired range from the original array at the same time you are subtracting from your original array until is empty.
const originalArr = [1,2,3,4,5,6,7,8,9,10,11];
const splittedArray = [];
while (originalArr.length > 0) {
splittedArray.push(originalArr.splice(0,range));
}
output for range 3
splittedArray === [[1,2,3][4,5,6][7,8,9][10,11]]
output for range 4
splittedArray === [[1,2,3,4][5,6,7,8][9,10,11]]
This is also good for a fronted pagination if want.
Another method:
var longArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var size = 2;
var newArray = new Array(Math.ceil(longArray.length / size)).fill("")
.map(function() { return this.splice(0, size) }, longArray.slice());
// newArray = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]];
This doesn't affect the original array as a copy, made using slice, is passed into the 'this' argument of map.
Another implementation, using Array.reduce (I think it’s the only one missing!):
const splitArray = (arr, size) =>
{
if (size === 0) {
return [];
}
return arr.reduce((split, element, index) => {
index % size === 0 ? split.push([element]) : split[Math.floor(index / size)].push(element);
return split;
}, []);
};
As many solutions above, this one’s non-destructive. Returning an empty array when the size is 0 is just a convention. If the if block is omitted you get an error, which might be what you want.
More compact:
const chunk = (xs, size) =>
xs.map((_, i) =>
(i % size === 0 ? xs.slice(i, i + size) : null)).filter(Boolean);
// Usage:
const sampleArray = new Array(33).fill(undefined).map((_, i) => i);
console.log(chunk(sampleArray, 5));
function chunkArrayInGroups(arr, size) {
var newArr=[];
for (var i=0; i < arr.length; i+= size){
newArr.push(arr.slice(i,i+size));
}
return newArr;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6], 3);
Using ES6 Generators
Late to the party, but ES6 generators opened up another neat way to achieve what is asked for.
/**
* Returns chunks of size n.
* #param {Array<any>} array any array
* #param {number} n size of chunk
*/
function* chunks(array, n){
for(let i = 0; i < array.length; i += n) yield array.slice(i, i + n);
}
const result = [...chunks([1, 2, 3, 4, 5, 6, 7, 8 , 9, 10], 3)];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Make it work for infinite Generators
Using the same idea you can create a generator which can also generate an infinite amount of n-sized chunks from values retrieved from another (possibly infinite) generator function. This can be very handy to lazy-generate values once they are required which significantly reduces the required memory or it can even be used to generate a possibly infinite/ unknown number of chunks.
Here an example which uses two generators.
nextNaturalNumber() is an infinite generator which always returns the next natural number. I am using the ES2020 bigint datatype here so there is no restriction (by JavaScript) for the size of the value.
chunksFromIterable() creates n-sized chunks from an possibly infinite iterable.
/**
* Returns chunks of size n for a possibly infinite iterator.
* n must be >= 1
* #param {Iterable<any>} iterable any array
* #param {number} n size of chunk for n >= 1
*/
function* chunksFromIterable(iterable, n){
let arr = [];
let i = n;
for (const value of iterable) {
if(i <= 0) {
// another chunk of size n is filled => return chunk
yield arr;
arr = []; // create new empty array
i = n;
};
arr.push(value);
i--;
}
// in case the iterable is not infinite check if there are still values in the array and return them if necessary
if(arr.length > 0) yield arr;
}
/**
* Infinite iterator which always gets the next natural number.
*/
function* nextNaturalNumber(){
let i = 0n;
while(true) {
i += 1n;
yield i;
}
}
console.log("Finite iterable:");
// this version can now be used using the for ... of loop
for(const threeNaturalNumbers of chunksFromIterable([1, 2, 3, 4, 5, 6, 7, 8 , 9, 10], 3)){
console.log(threeNaturalNumbers);
}
console.log("Infinite iterable:");
// and it can also be used for this infinite generator
for(const threeNaturalNumbers of chunksFromIterable(nextNaturalNumber(), 3)){
printBigIntArray(threeNaturalNumbers);
if(threeNaturalNumbers[0] > 30) break; // end here to avoid an infinite loop
}
// helper function to print array of bigints as this does not seem to be working for snippets
function printBigIntArray(arr){
console.log(`[${arr.join(", ")}]`);
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can take a look at this code . Simple and Effective .
function chunkArrayInGroups(array, unit) {
var results = [],
length = Math.ceil(array.length / unit);
for (var i = 0; i < length; i++) {
results.push(array.slice(i * unit, (i + 1) * unit));
}
return results;
}
chunkArrayInGroups(["a", "b", "c", "d"], 2);
Here is a simple one liner
var segment = (arr, n) => arr.reduce((r,e,i) => i%n ? (r[r.length-1].push(e), r)
: (r.push([e]), r), []),
arr = Array.from({length: 31}).map((_,i) => i+1);
console.log(segment(arr,7));
as a function
var arrayChunk = function (array, chunkSize) {
var arrayOfArrays = [];
if (array.length <= chunkSize) {
arrayOfArrays.push(array);
} else {
for (var i=0; i<array.length; i+=chunkSize) {
arrayOfArrays.push(array.slice(i,i+chunkSize));
}
}
return arrayOfArrays;
}
to use
arrayChunk(originalArray, 10) //10 being the chunk size.
using recursion
let myArr = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16];
let size = 4; //Math.sqrt(myArr.length); --> For a n x n matrix
let tempArr = [];
function createMatrix(arr, i) {
if (arr.length !== 0) {
if(i % size == 0) {
tempArr.push(arr.splice(0,size))
}
createMatrix(arr, i - 1)
}
}
createMatrix(myArr, myArr.length);
console.log(tempArr);
Note: The existing array i.e. myArr will be modified.
using prototype we can set directly to array class
Array.prototype.chunk = function(n) {
if (!this.length) {
return [];
}
return [this.slice(0, n)].concat(this.slice(n).chunk(n));
};
console.log([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].chunk(5));
let original = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
let size = 5;
let fragments = Array.from(Array(Math.ceil(a.length / size))).map((_,index) => a.slice(index * size,(index + 1) * size))
If you want a method that doesn't modify the existing array, try this:
let oldArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
let newArray = [];
let size = 3; // Size of chunks you are after
let j = 0; // This helps us keep track of the child arrays
for (var i = 0; i < oldArray.length; i++) {
if (i % size === 0) {
j++
}
if(!newArray[j]) newArray[j] = [];
newArray[j].push(oldArray[i])
}
function chunkArrayInGroups(arr, size) {
var newArr=[];
for (var i=0; arr.length>size; i++){
newArr.push(arr.splice(0,size));
}
newArr.push(arr.slice(0));
return newArr;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6], 3);
You can use the following code to achieve the required functionality
const splitter = (arr, splitBy, cache = []) => {
const tmp = [...arr]
while (tmp.length) cache.push(tmp.splice(0, splitBy))
return cache
}
const split = splitter([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21], 10)
console.log(split);
Notice, it was an array of length 22 then the splitter function splits it into 2 smaller arrays of 10 items and 1 array of 2 items.
let a = [1, 2, 3, 4, 6, 7, 8, 9, 10, 11];
let _splitCount = 3;
let b = a.reduce((acc, curr, index) => {
if (index % _splitCount === 0) {
acc.push([curr]);
} else {
acc[acc.length - 1].push(curr);
}
return acc;
}, []);
this is the easy solution i think❤️
You can use the below function if you know the number array (numGroups) to be split.
function createGroups(arr, numGroups) {
const perGroup = Math.ceil(arr.length / numGroups);
return new Array(numGroups)
.fill('')
.map((_, i) => arr.slice(i * perGroup, (i + 1) * perGroup));
}
Sample Use:
createGroups([0, 1, 2, 3, 4, 5, 6], 3); //arr = [0, 1, 2, 3, 4, 5, 6] and numGroups = 3

Categories

Resources