Related
I have an array of arrays of different sizes. The goal is to generate "rows" where each row can contain a max of 12 elements.
For example:
Input data can be something like this:
const groups = [[1,2,3,4],[1,2,3,4,5,6], [1,2,3,4,5,6,7,8,9,10,11,12], [1,2,3,4,5,6,7], [1,2,3],[1,2,3]]
groups[0].length + groups[1].length = 10 -> row0
groups[2].length = 12 -> row1
groups[3].length + groups[4].length = 10 -> row3
groups[5].length = 3 -> row4
Output for such array should be:
[[[1,2,3,4], [1,2,3,4,5,6]], [[1,2,3,4,5,6,7,8,9,10,11,12]], [[1,2,3,4,5,6,7], [1,2,3]], [[1,2,3]]]
I was thinking of a recursive function for this but couldn't figure out how to solve it.
You can use Array#reduce() to do this.
The code first checks if the current last element (last "row") has more than 12 numbers in it if you add the next group:
(acc[acc.length - 1].flat().length + cv.length) <= 12
if it will be less than 12, the elements will get pushed into the "row":
acc[acc.length - 1].push(cv)
and if not, a new "row" will be added to the outer array:
acc.push([cv])
const groups = [[1, 2, 3, 4],[1, 2, 3, 4, 5, 6],[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],[1, 2, 3, 4, 5, 6, 7],[1, 2, 3],[1, 2, 3]];
const rows = groups.reduce((acc, cv) => {
(acc[acc.length - 1].flat().length + cv.length) <= 12 ?
acc[acc.length - 1].push(cv) :
acc.push([cv])
return acc
}, [[]]);
console.log(JSON.stringify(rows))
Here's one way to solve it recursively:
const regroup = (max, [g, ...gs], filled = [], curr = [], cc = 0) =>
g == undefined
? filled .concat ([curr])
: g .length + cc <= max
? regroup (max, gs, filled, curr .concat ([g]), cc + g.length)
: regroup (max, gs, filled .concat ([curr]), [g], g .length)
const groups = [[1, 2, 3, 4], [1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
[1, 2, 3, 4, 5, 6, 7], [1, 2, 3], [1, 2, 3]]
console .log (regroup (12, groups))
.as-console-wrapper {max-height: 100% !important; top: 0}
We pass the maximum size and the list of items, and then we default three parameters:
filled will track the output rows we've filled; it starts with an empty array
curr stores the row we're working on; it also starts with an empty array
cc stores the count of all elements in the current row; it starts with zero
On each recursive call, we have one of three possibilities:
There are no more arrays to process, and we return all the filled rows with the current row appended.
The next array is small enough to fit in the current row, and we update the current to include it, and the current count to accommodate it.
The next array is too large, and we add the existing current row to the filled ones, and start a new current row with this array, setting the count appropriately.
With an array of: [1, 2, 3, 4, 5, 6]
I would like to delete between 2 indices such as 2 and 4 to produce [1, 2, null, null, 5, 6]. What's the easiest way to do this?
Hopefully better than this:
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
let i = 2;
const rangeEnd = 9;
while (i < rangeEnd) {
delete array[i];
i++;
}
console.log(array)
If you want to use some native API you can actually do this with splice(). Otherwise, you should iterate a for loop through your array and change the value in each iteration.
Here is an example of how it would be done:
const array = [1, 2, 3, 4, 5, 6]
array.splice(3, 2, null, null) // the First element is beginning index and the second is count one will indicate how many indexes you need to traverse from the first one, then you should provide replace element for each of them.
console.log(array)
Note: For more info about it you can read more here.
There is a possible workaround for large scale replacement, so I will give it a touch here:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var anotherArr = Array(2).fill(null); // or you can simply define [null, null, ...]
Array.prototype.splice.apply(arr, [3, anotherArr.length].concat(anotherArr));
console.log(arr);
As you mean the range (2, 4] so you can follow this:
The range is: lower limit exclusive and the upper limit inclusive.
const arr = [1, 2, 3, 4, 5, 6];
const deleteRange = (arr, f, t) => {
return arr.map((item, i) => {
if (i + 1 > f && i + 1 <= t) {
return null;
}
return item;
})
}
console.log(deleteRange(arr, 2, 4));
previousValue currentValue index array return value
first call 0 1 1 [0, 1, 2, 3, 4] 1
second call 1 2 2 [0, 1, 2, 3, 4] 3
third call 3 3 3 [0, 1, 2, 3, 4] 6
fourth call 6 4 4 [0, 1, 2, 3, 4] 10
I want 1,3,6,10 in an array not the return total 10. So to return each call
You can push the return value into an array, like this. It goes against functional programming since it mutates results as a side effect. But it does meet your needs.
var array = [0, 1, 2, 3, 4];
var results = [];
array.reduce(function(previousValue, currentValue) {
var newValue = previousValue + currentValue;
results.push(newValue);
return newValue;
});
// result is 1,3,6,10
alert(results);
Don't use reduce for this. Slice the array, shift a value to start a subtotal, then use map.
var arr = [0, 1, 2, 3, 4], output = arr.slice(), subtotal = output.shift()
output = output.map(function(elem) { return subtotal += elem })
// output is [1, 3, 6, 10]
Edit - Actually, this could work fine with reduce, and even more concise than the above:
var arr = [0, 1, 2, 3, 4]
arr.reduce(function(a, b, ndx) { return a.length ? a.concat(a[ndx - 2] + b) : [a + b]})
// returns [1, 3, 6, 10]
I am now writing a javascript to filter out arrays that contains a specific sub-array. Of course I can write the function by myself, but I am just curious if there are already some built-in function in javascript or other javascript library to do that, or if there are easy way to do that with just a few lines.
I found that I can use underscore.js if all the elements in the sub-array is unique. There is a intersection function and I can check the lenght after intersection to see if the length are correct. However, that function fails if there are repeated values in the sub-array.
For example,
_.intersection([1, 2, 3, 4, 5], [2, 1]);
This will return [1, 2] and by checking the length I will know this array contains the sub-array.
However, when there are repeated values in the sub-array,
_.intersection([1, 1, 2, 3, 4, 7, 10], [1, 1, 2]);
_.intersection([1, 2, 3, 4], [1, 1, 2]);
Both will return [1, 2] and the cases cannot be distinguished.
Is there other pre-built function I can use or is there a easy way to do the job within a few lines?
Try this:
function contains(a, b) {
// sort arguments
if(a.length < b.length) {
var temp = a;
a = b;
b = temp;
}
// copy array
a = a.slice();
return b.every(function(elm) {
var index = a.indexOf(elm);
if(index !== -1) {
// remove the found element
a.splice(index, 1);
return true;
}
return false;
});
}
console.log(contains([1, 1, 2], [1, 2, 3, 4, 7, 10])); // logs false
console.log(contains([1, 1, 2], [1, 1, 2, 3, 4, 7, 10])); // logs true
console.log(contains([1, 2, 3, 4, 7, 10], [1, 1, 2])); // logs false
console.log(contains([1, 1, 2, 3, 4, 7, 10], [1, 1, 2])); // logs true
Here is the demo
I have an array and would like to sort all but the last n elements.
For example, if the array is 10 elements long, would like elements 0 through 7 to be sorted while elements 8-9 are left in place.
var array = [5, 2, 6, 4, 1, 9, 3, 8, 7];
array = array.slice(0, 7).sort().concat(array.slice(7, 10));
// array is now [1, 2, 3, 4, 5, 6, 9, 8, 7]
If you need to sort the array in place (i.e. without creating a new, sorted array), which is what the sort() method does, you could do the following:
var array = [5, 2, 6, 4, 0, 1, 9, 3, 8, 7];
var unsorted = array.slice(7);
array.length = 7;
array.sort().push.apply(array, unsorted);
More generally, here's a function to sort a portion of an array in place. Like the sort() method, it also returns a reference to the array.
function partialSort(arr, start, end) {
var preSorted = arr.slice(0, start), postSorted = arr.slice(end);
var sorted = arr.slice(start, end).sort();
arr.length = 0;
arr.push.apply(arr, preSorted.concat(sorted).concat(postSorted));
return arr;
}
Example:
var array = [5, 2, 6, 4, 0, 1, 9, 3, 8, 7];
partialSort(array, 0, 7);
An ES6 riff on the solution provided by #darin
let subSort = (arr, i, n, sortFx) => [].concat(...arr.slice(0, i), ...arr.slice(i, i + n).sort(sortFx), ...arr.slice(i + n, arr.length));
i is the index where the subsection begins
n is the number of elements to sort
sortFx is the sorting function
So it's possible to sort a range within an array:
var array = [5, 2, 6, 4, 1, 9, 3, 8, 7];
// sort array beginning at index 2; sort 4 elements of array
subSort(array, 2, 4, (a, b) => a - b);
// array is now [5, 2, 1, 4, 6, 9, 3, 8, 7]
subSort(array, 2, 4, (a, b) => b - a);
// array is now [5, 2, 9, 6, 4, 1, 3, 8, 7]
subSort() can be used for objects of arbitrary complexity.
let arr = [2, 1, 5, 4, 3];
arr = [...arr.slice(0, 2), ...arr.slice(2).sort((a, b) => a - b)];
After sorting a sub-array the original array will be [2, 1, 3, 4, 5]