Split array up into n-groups of m size?
Use Enumerable#each_slice
.
a.each_slice(3).to_a
Or, to iterate (and not bother with keeping the array):
a.each_slice(3) do |x,y,z|
p [x,y,z]
end
Splitting an array into groups
You modify size inside the loop. This basically doubles the size at every step. The fact that your example only has two sub-arrays hides this problem. Try larger arrays to see the problem in the result.
To fix this, declare j outside of the loop. Add the size to j after slicing.
Edit: Code, as requested
function chunkArrayInGroups(arr, size) {
var result = [];
var j = 0;
for (var i = 0; i < Math.ceil(arr.length / size); i++) {
result[i] = arr.slice(j, j + size);
j = j + size;
}
return result;
}
Or, slightly simplified:
function chunkArrayInGroups(arr, size) {
let result = [];
let pos = 0;
while (pos < arr.length) {
result.push(arr.slice(pos, pos + size));
pos += size;
}
return result;
}
How to split a long array into smaller arrays, with JavaScript
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.
Split array into chunks
The array.slice()
method can extract a slice from the beginning, middle, or end of an array for whatever purposes you require, without changing the original array.
const chunkSize = 10;
for (let i = 0; i < array.length; i += chunkSize) {
const chunk = array.slice(i, i + chunkSize);
// do whatever
}
The last chunk
may be smaller than chunkSize
. For example when given an array
of 12 elements the first chunk will have 10 elements, the second chunk only has 2.
Note that a chunkSize
of 0
will cause an infinite loop.
How to split an array into even length chunks?
With this solution you get evenly split array items until the last item which incorporates any left over items (collection of items that's length is less than the chunk size.)
const a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]const chunk = 4
const chunk_array = (a, c) => { let arr = [] a.forEach((_, i) => { if (i%chunk === 0) arr.push(a.slice(i, i+chunk)) }) const [left_overs] = arr.filter(a => a.length < chunk) arr = arr.filter(a => a.length >= chunk) arr[arr.length-1] = [...arr[arr.length-1], ...left_overs] return arr}
console.log( chunk_array(a, chunk))
Splitting array into groups using typescript/javascript
Use Array.reduce()
You can run a .reduce() method on your products array, like so:
var products = [ { 'id': 1, name: 'test' }, { 'id': 2, name: 'test' }, { 'id': 3, name: 'test' }, { 'id': 4, name: 'test' }, { 'id': 5, name: 'test' }, { 'id': 6, name: 'test' }]
var numberOfObjects = 4 // <-- decides number of objects in each group
var groupedProducts = products.reduce((acc, elem, index) => { var rowNum = Math.floor(index/numberOfObjects) + 1 acc[`row${rowNum}`] = acc[`row${rowNum}`] || [] acc[`row${rowNum}`].push(elem) return acc}, {})
console.log(groupedProducts)
Split an array into smaller chunks of arrays with the length provided by size. Need explanation for solution with modulo
Instead of using your (definitely more efficient, and also clearer) approach of taking consecutive slices, this takes the array item by item, storing the next group to be added in a temporary array. The test is basically asking (with a weird negative test) "Is this index not the last one in the group?" If that condition is true, and we're not on the last one for the current group, we simply add to the temporary group. If it's false, and we are on the last one, we add to the temporary group, add the temporary group to our output and create a new temporary group.
With a little refactoring, we can make this cleaner, perhaps something like this:
function chunkArrayInGroups(arr, size) {
const result = []
let temp = []
for (let a = 0; a < arr .length; a ++) {
temp .push (arr [a])
if (a % size === size - 1) {
result .push (temp)
temp = []
}
}
if (temp .length > 0) result .push (temp)
return result
}
But this holds no advantages over your approach, and is definitely less efficient. So I wouldn't consider it.
Here's an alternative that is to my mind cleaner, but also less efficient than yours:
const chunk = (xs, n) => xs .length <= n
? [[...xs]]
: [xs .slice (0, n)] .concat (chunk (xs .slice (n), n))
This one grabs the first group by slicing it from the beginning of the array, and concatenates the results of recursively calling itself with the remainder of the array. The recursion ends when there are at most n
elements remaining, and then we return an array containing only a copy of our input array ([[...x]]
). We make the copy because all the other results are copies and not references, and it's better to be consistent.
This is similar to your approach in terms of number of slices and tests, but because of the recursive nature, it will fail for very large arrays and the repeated calls will make it somewhat less efficient. I'm often willing to take that tradeoff for cleaner code, myself, but YMMV.
Split Array of items into N Arrays
I'm not 100% sure how this should work on different sized arrays with different group counts, but this works for your 12 digit example:
function chunkArray(arr, chunkCount) { const chunks = []; while(arr.length) { const chunkSize = Math.ceil(arr.length / chunkCount--); const chunk = arr.slice(0, chunkSize); chunks.push(chunk); arr = arr.slice(chunkSize); } return chunks;}
var arr = [1,2,3,4,5,6,7,8,9,10,11,12];console.log( chunkArray(arr, 5) )
Splitting String Arrays into Groups
I would suggest changing your code to calculate the number of groups you need to this:
int groups = (count / groupSize);
bool hasPartialGroup = count % groupSize != 0;
if (hasPartialGroup)
{
++groups;
}
The result of the first line will be integer division, so 15 / 6 will result in 2. We then see if there is a remainder using the remainder operator (%
): count % groupSize
. If its result isn't 0, then there is a remainder, and we have a partial group, so we have to account for that.
So for groups = 15
and groupSize = 6
, we'll get count = 3
. For groups = 12
and groupSize = 6
, we'll get count = 2
, etc.
Fixing your code to use this, it might look like:
string[] tags = {"1","2","3","4","5","6","7","8","9","10","11","12","13","14","15"};
int count = tags.Length;
const int groupSize = 6;
int groups = (count / groupSize);
bool hasPartialGroup = count % groupSize != 0;
if (hasPartialGroup)
{
++groups;
}
for (int i = 0; i < groups; i++)
{
// you can't copy beyond the end of the array so we have to choose between the remaining ungrouped items and the group size
int currentGroupSize = Math.Min(tags.Length - i*groupSize, groupSize);
// I'm assuming for a partial group you only want this to be as big as the number of items.
// If you want it to always be 6 then change new string[currentGroupSize] to new string[groupSize] and you should be OK.
string[] groupArrays = new string[currentGroupSize];
Array.Copy(tags, i * groupSize, groupArrays, 0, currentGroupSize);
Console.WriteLine(string.Join(",", groupArrays));
}
Try it online // Example with fixed group size
Alternatively, you could create a batching helper method:
private static IEnumerable<T[]> BatchItems<T>(IEnumerable<T> source, int batchSize)
{
var collection = new List<T>(batchSize);
foreach (var item in source)
{
collection.Add(item);
if (collection.Count == batchSize)
{
yield return collection.ToArray();
collection.Clear();
}
}
if (collection.Count > 0)
{
yield return collection.ToArray();
}
}
This will collect batchSize
number items together and then return one group at a time. You can read about how this works with yield return
here.
Usage:
string[] tags = {"1","2","3","4","5","6","7","8","9","10","11","12","13","14","15"};
List<string[]> batchedTags = BatchItems(tags, 6).ToList();
This will result in 3 string arrays, containing 1,2,3,4,5,6
, 7,8,9,10,11,12
, and 13,14,15
.
You could also make this into an extension method.
Try it online
Related Topics
Activeadmin Forbiddenattributeserror
Referencing Model with String Input
Ruby Time Object Converted from Float Doesn't Equal to Orignial Time Object
Rails 4 Strong Parameters Failing When Creating Instances in Rails Console
Ruby Rest-Client File Upload as Multipart Form Data with Basic Authenticaion
Ruby Sandboxing VS. Integrating a Scripting Language
How to Get Words Frequency in Efficient Way with Ruby
"Certificate Verify Failed" Error When Installing Ruby Gems on Windows
Can't Launch Simple Sinatra App Using Rackup and Jruby (No Response from Web Server)
How to Pass a Variable to a Layout
How to Make a Non-Blocking Request for an Exclusive Lock Using File#Flock