This question already has answers here:
How to split a long array into smaller arrays, with JavaScript
(26 answers)
Split array into chunks
(73 answers)
Closed 4 years ago.
I am trying to split an array of objects into sets of 3 and there is a slight problem. I run the code and it splits the first group into 2 and the rest into 3. I want the last group to have the remaining elements. So for instance if there are 21 objects then the last group should have 2 but instead the first group is the one with 2. How can I make the last group be the one with the remaining objects?
var newData = [];
var setOfThree = [];
for (i = 0; i < data.length; i++) {
setOfThree.push(data[i]);
if (i % 3 == 1) {
newData.push(setOfThree);
setOfThree = [];
}
}
So the data ends up looking like this:
Here's a very clean and efficient solution (leverages the fact that Array.prototype.slice automatically truncates results):
let groupByN = (n, data) => {
let result = [];
for (let i = 0; i < data.length; i += n) result.push(data.slice(i, i + n));
return result;
};
console.log(JSON.stringify(groupByN(3, [ 1 ])));
console.log(JSON.stringify(groupByN(3, [ 1, 2 ])));
console.log(JSON.stringify(groupByN(3, [ 1, 2, 3 ])));
console.log(JSON.stringify(groupByN(3, [ 1, 2, 3, 4 ])));
console.log(JSON.stringify(groupByN(3, [ 1, 2, 3, 4, 5 ])));
console.log(JSON.stringify(groupByN(3, [ 1, 2, 3, 4, 5, 6 ])));
console.log(JSON.stringify(groupByN(3, [ 1, 2, 3, 4, 5, 6, 7 ])));
console.log(JSON.stringify(groupByN(3, [ 1, 2, 3, 4, 5, 6, 7, 8 ])));
The first array gets 2 items because, when i === 1 1%3 will result to 1
Starting from counter 1 could be one solution
data = [1,2,3,4,5,6,7]
var newData = [];
// var setOfThree = []; // not required
var j = 0
newData.push([]);
//pushing at the very begining, this way we won't miss if the data
// is not in groups of 3
for (i = 1; i <= data.length; i++) {
// always updating the final array
newData[j].push(data[i-1]);
if (i % 3 == 0) {
newData.push([]);
j++;
}
}
if (newData[0].length === 0) {
// if the data you received was epmty
newData.pop()
}
console.log(newData)
Here a recursive implementation, with some es6 sugar
var input = [1,2,3,4,5,6,7,8]
function groupByThree([a,b,c,...rest]){
if (rest.length === 0) return [[a,b,c].filter(x => x!==undefined)]
return [[a,b,c]].concat(groupByThree(rest))
}
console.log(groupByThree(input))
Related
Code War problem
My Solution on this problem:
function numberOfPairs(gloves) {
const glove = gloves.slice().sort();
const pairs = [];
for (let i = 0; i < glove.length - 1; i++) {
if (glove[i] == glove[i+1]) {
pairs.push(glove[i]);
}
}
return pairs.length;
}
It pass the initial test but failed the attempt/random test.
2nd test
My Second Solution, I add i++. It pass the Attempt
function numberOfPairs(gloves) {
const glove = gloves.slice().sort();
const pairs = [];
for (let i = 0; i < glove.length - 1; i++) {
if (glove[i] == glove[i+1]) {
pairs.push(glove[i]);
i++ // How?
}
}
return pairs.length;
}
Can you guys help me how does i++ fix the problem.
You can also group the colors and then count the pairs using Array.prototype.reduce.
function solution(input) {
return input.reduce(
([pairs, hash], clr) => {
hash[clr] = (hash[clr] ?? 0) + 1;
return [pairs + (hash[clr] % 2 === 0 ? 1 : 0), hash];
},
[0, {}]
)[0];
}
console.log(solution(["red", "green", "red", "blue", "blue"]));
console.log(solution(["red", "red", "red", "red", "red", "red"]));
Because if you found a pair, the next element is equal to the actual and you're advancing only one element in the array at a time. If you found a pair, the next element was the one found, then you have to advance another position in the array when you found it.
Say you have this array: [1, 2, 5, 8, 9, 2, 8]
Let's number the lines to make it easier:
1 function numberOfPairs(gloves) {
2 const glove = gloves.slice().sort();
3 const pairs = [];
4 for (let i = 0; i < glove.length - 1; i++) {
5 if (glove[i] == glove[i+1]) {
6 pairs.push(glove[i]);
7 i++ // How?
8 }
9 }
10 return pairs.length;
11 }
after line 2 you will have the following sorted array:
[1, 2, 2, 5, 8, 8, 9]
with 2 pairs in it.
Then you will start iteration of the array with i = 0 so in line 6 in the first iteraction glove[i] is 1 and glove[i+1] is 2. Different, so the for continues to i = 1.
Now the if in line 6 tests glove[i] with i = 1 making glove[i] = 2 and glove[i+1] also = 2 (first pair). So it enters the if and pushes the first number to pairs: 2.
Now if i++ isn't present in line 7, what would happen is that for would countinue to i = 2 making glove[i] = 2 and glove[i + 1] = 5. But the 2 makes part of the first pair encountered. So this is incorrect. We must skip the 2nd 2 that's what i++ is line 7 is there for. It would be even worse if the 4th element in the array was 2 again because it would inform another pair when there is only one.
After this for will continue not to i=2 but correctly to i=3 testing elements 5 and 8 for the next pair.
Hope this explains clearly enough.
So this function is supposed to split the array into chunks the size of the second argument, e.g. [1, 2, 3, 4] with the second argument 2 would return [[1, 2], [3, 4]], if the number is 3 it would return [[1, 2, 3], [4]] and so on. My function seems to be behaving in a similar way but it only returns the first chunk, and the subsequent chunks are just empty arrays. I increase the index by by the second argument after each iteration, so I'm not sure why it's not working. Can someone please explain what exactly is happening here and where is the error in the logic?
let arr = [1, 2, 3, 4, 5, 6]
function test(arr, num) {
let idx = 0;
let newArr = []
while (idx < arr.length) {
newArr.push(arr.slice(idx, num))
idx = idx + num
}
return newArr
}
console.log(test(arr, 2))
You need an index as second parameter of Array#slice, instead of the length of slice.
For example if you take the second array, index = 2 and the second parameter has to be 4, the index of the end for slicing.
chunks
values 1 2 3 4 5 6
indices 0 1 2 3 4 5
slice 0 2 1 2
2 4 3 4
4 6 5 6
function test(arr, num) {
let idx = 0;
let newArr = [];
while (idx < arr.length) {
newArr.push(arr.slice(idx, idx += num));
}
return newArr;
}
let arr = [1, 2, 3, 4, 5, 6];
console.log(test(arr, 2));
This question already has answers here:
Rotate the elements in an array in JavaScript
(42 answers)
Closed 4 years ago.
I have an array,
var myArray = [ 1,2,3,4,5 ]
and variable count,
var count = 5
Pseudocode :
if count = 1, output myArray = [5,1,2,3,4]
if count = 2, then myArray = [ 4,5,1,2,3]
and so on ..
How can I achieve this without using loops ?
You could slice with a negative index from the end the array for the last part and the first part and concat a new array.
function move(array, i) {
return array.slice(-i).concat(array.slice(0, -i));
}
var array = [1, 2, 3, 4, 5];
console.log(move(array, 1)); // [5, 1, 2, 3, 4].
console.log(move(array, 2)); // [4, 5, 1, 2, 3]
.as-console-wrapper { max-height: 100% !important; top: 0; }
Use pop for removing last item of the array and unshift for adding it at the beginning of the arrray then
const count = 2;
const myArray = [ 1,2,3,4,5 ];
for (let i = 0; i < count; i++) {
myArray.unshift(myArray.pop());
}
console.log(myArray);
This question already has answers here:
Split array into chunks
(73 answers)
Closed 4 years ago.
I'm trying to implement a chunk function in javascript similar to lodash chunk. It seems like i'm hitting an indexing issue relating to the count here but I can't figure it out.
// chunk array function breaks an array into chunks of defined size
// [1, 2, 3, 4, 5, 6, 7, 8]
// with size 2
// should output: [[1,2], [3,4], [5,6], [7,8]]
const testArr = [1, 2, 3, 4, 5, 6, 7, 8]
const testArr2 = [1, 2, 3, 4, 5, 6, 7]
function chunk(arr, size){
let newArr = []
let tempArr = []
let iterations;
let remainder;
if(Number.isInteger(arr.length / size)){
iterations = arr.length / size
} else {
iterations = size.toString().split('.')[0]
// how many remain?
remainder = arr.length % size
}
// theres an issue somewhere in here relating to count
let count = 0
while(count < iterations){
tempArr = []
for(let i = count; i < (size + count); i++){
tempArr.push(arr[i])
}
newArr.push(tempArr)
count++
}
// if(remainder){
// for(let i = count; i < count + remainder; i++){
// tempArr.push(arr[i])
// }
// }
return newArr
}
console.log(chunk(testArr, 2))
I'm interested in 2 different things:
Whats wrong with my code example?
How would YOU implement this better? Clearly my example is not very
clean and I'm curious how others would handle it (some .map
.reduce stuff maybe?) i tried reading lodash docs but they use a lot of internal functions that make it a little confusing.
actual output is: [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ] ]
output should be: [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ] ]
Thanks!
A simpler way to do this would be:
let size = 2;
[1, 2, 3, 4, 5, 6, 7, 8].reduce((carry, current, index) => {
// get the current array bucket. if it doesn't exist, create it.
let el = carry[Math.floor(index / size)] = carry[Math.floor(index / size)] || [];
// push the current element onto the current bucket
el.push(current);
// return our new array
return carry;
}, [])
The issue with your code is just that you need to do:
tempArr.push(arr[i + count])
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I wrote this Matlab code to generate a vector of random [1 0] and [2 0]:
nTrials = 8;
Seq1 = [1 0]; % sequence 1
Seq2 = [2 0]; % sequence 2
a=repmat(Seq1,(nTrials/4),1);
b=repmat(Seq2,(nTrials/4),1);
abcd = vertcat(a,b); % concatenate all three couples
CouplesOrderStruct = abcd(randperm(size(abcd,1)),:); % couples in columns
vector = (reshape(CouplesOrderStruct.',[],1))';
The result is a vector like: [1 0 2 0 2 0 1 0]
Code explained:
I have two sequences of numbers, 1-0 and 2-0, which I want to randomize in my vector.
First, in a = repmat(Seq1,(nTrials/4),1); b=repmat(Seq2,(nTrials/4),1); I create a fixed amount of sequences
Second, I put a and b together: abcd = vertcat(a,b); % concatenate all three couples
Third, I randomize these sequences in CouplesOrderStruct = abcd(randperm(size(abcd,1)),:);
The results is a vector with the same amount of 1-0 and 2-0, but in a random order
Is there a way to get the same result with JavaScript?
Sooo i just built a nice tiny documented function for you:
function randomRepeatSequence(sequences, times) {
// times has to be a multiple of sequences.length
if (times % sequences.length !== 0)
return console.log("times has to be a multiple of sequences.length");
// Remap our sequence-array so we can store the count it has been used
var seqmap = [];
for (var seqid = 0; seqid < sequences.length; seqid++)
// Push the current sequence n times; n = times/sequences.length
for (var idx = 0; idx < times/sequences.length; idx++)
seqmap.push(sequences[seqid]);
var resultmap = [];
// Now just select and remove a random sequence from our seqmap, until it is empty
while (!seqmap.length == 0) {
// Select a random element
var randomidx = Math.floor(Math.random()*seqmap.length);
var currentElement = seqmap[randomidx];
// remove the random element from seqmap...
seqmap.splice(randomidx, 1);
// .. and push it to the resultmap
resultmap.push(currentElement);
}
// now our resultmap looks like [[1],[2],[3]]... just flatten it!
var result = resultmap.reduce( function(a, b) {
return a.concat(b);
});
return result;
}
You can use it just like
console.log(randomRepeatSequence([[1,0], [2,0]], 4));
Or, better to understand:
var seqlist = [
[1, 0],
[2, 0]
]
randomRepeatSequence(seqlist, 4)
Please care, the times parameter just takes the amount of sequences that have to be used, not the length of the result. But you just have to calculate that in a easy step like
randomRepeatSequence(seqlist, 8/seqlist[0].length)
(giving 4, because seqlist[0].length = 2 and 8 / 2 is 4)
Original Answer
Your result is for example
vector = 2 0 1 0 2 0 1 0
I guess seq1 and seq2 should be contained equal times.
I decided to use an easy-to-understand-approach, even through I can do shorter:
var trials = 8; // has to be even
var seq1 = [1, 0];
var seq2 = [2, 0];
// "Build" a sequence list
var seqlist = [
seq1, seq1,
seq2, seq2
]
var list = []
for (var n = 0; n < trials/2; n++) {
// search a random entry
var index = Math.floor(Math.random()*seqlist.length);
var toUseSeq = seqlist[index];
// delete the entry
seqlist.splice(index, 1);
list.push(toUseSeq);
}
// flatten result array
var result = list.reduce( function(a, b) {
return a.concat(b);
});
console.log(result);
Executing this gaves me one of these console outputs:
[ 2, 0, 1, 0, 2, 0, 1, 0 ]
[ 2, 0, 1, 0, 2, 0, 1, 0 ]
[ 1, 0, 1, 0, 2, 0, 2, 0 ]
[ 1, 0, 2, 0, 1, 0, 2, 0 ]