How to slice an array in a custom pattern? - javascript

I have this array:
var x = [1,2,3,4,5,"a","b","c",9,10];
I would like to slice this array into this pattern:
var x = [[1,2,3],[2,3,4],[3,4,5],[4,5,"a"],[5,"a","b"],["a","b","c"],["b","c",9],["c",9,10]];
I used the following code and been able to get [[1,2,3],[4,5,"a"],["b","c",9],[10,11,12]] . But it doesn't work for me. I need to get the pattern above.
var stream = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var x = ["a", "b", "c"];
var ad_time = 6;
var result = [];
var ad_index = stream.indexOf(ad_time);
if (~ad_index) {
for (var i = 0; i < x.length; i++) {
stream[ad_index + i] = x[i];
}
}
while (stream.length > 0) {
var chunk = stream.splice(0, 3);
result.push(chunk);
}
console.log(JSON.stringify(result));
Thanks in advence!

This code should do it:
var x = [1,2,3,4,5,"a","b","c",9,10];
var new_array = [];
for (var i = 0; i < x.length - 2; i++) {
new_array.push(x.slice(i, i + 3));
}

You can achieve it with a simple for loop:
var x = [1,2,3,4,5,"a","b","c",9,10];
var result = [];
for (var i = 0, il = x.length - 2; i < il; i++) {
result.push([x[i], x[i + 1], x[i + 2]]);
}
console.log(result);
EDIT: Array.slice() is more elegant, however it is much slower. On Chrome it is 80% - 85% slower according to this test. If you don't need to worry about performance, choose whichever you like. For example if you need to slice 8 elements from the array, then using x.slice(i + 8) is easier to write and read than [x[i], x[i + 1], x[i + 2], x[i + 3], ...]. However if performance matters, then direct access might be a better choice.

Related

Array to Object with Values in a Range

I have a crazy requirement here. I am not sure how to make it work well, may be I thought of getting the community's help here. This is the first time here for me. Let me explain my situation clearly.
I have an array of, say 10 elements.
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].reverse();
Now I need to create an object of keys containing the array elements, but their values should be like:
First 5 to be "high".
Next 3 to be "medium".
Rest 2 to be "low".
I do have the above information in another array:
var capacity = [5, 3, 2];
The above three values (high, medium, low) are static. Not sure how to proceed from here. I am not sure if I should be using a for loop hardcoded like:
var obj = {};
for (var i = 0; i < capacity[0]; i++)
obj[arr[i]] = "high";
for (; i < capacity[0] + capacity[1]; i++)
obj[arr[i]] = "medium";
for (; i < capacity[0] + capacity[1] + capacity[2]; i++)
obj[arr[i]] = "low";
Not sure if this is the right way to proceed. Any pointers for the right direction? Thanks.
Snippet
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].reverse();
var capacity = [5, 3, 2];
var obj = {};
for (var i = 0; i < capacity[0]; i++)
obj[arr[i]] = "high";
for (; i < capacity[0] + capacity[1]; i++)
obj[arr[i]] = "medium";
for (; i < capacity[0] + capacity[1] + capacity[2]; i++)
obj[arr[i]] = "low";
console.log(obj);

find all subarrays in o(n) time 1D array using javascript

I want to collect all subarrays for further computation efficiently in javascript. I'm not sure this is possible, but it seems for a subarray sum kadane's formula is o(n) which is more efficient than other methods. But I'm not sure I how I can store the array at each step.
Similar to this quora question, for me the pseudo code was not enough. Thanks for the further breakdown.
another meta link
an example in action of this for [3, 3, 9, 9, 5]
[3], [9], [5], [9, 5], [9, 3], [9, 9], [3, 3],
[3, 9, 9], [3, 3, 9], [9, 9, 5], [3, 3, 9, 9],
[3, 9, 9, 5], [3, 3, 9, 9, 5]
I had done a work previously to calculate all combinations of amino acids total molecular weight. If you neglect the empty one you should have 2^n - 1 sub arrays. So there is no O(n) here. I've got two methods as binary and recursive.
function getSubArrays(arr){
var len = arr.length,
subs = Array(Math.pow(2,len)).fill();
return subs.map((_,i) => { var j = -1,
k = i,
res = [];
while (++j < len ) {
k & 1 && res.push(arr[j]);
k = k >> 1;
}
return res;
}).slice(1);
}
console.log(JSON.stringify(getSubArrays([1,2,3,4,5])));
function getSubArrays(arr){
if (arr.length === 1) return [arr];
else {
subarr = getSubArrays(arr.slice(1));
return subarr.concat(subarr.map(e => e.concat(arr[0])), [[arr[0]]]);
}
}
console.log(JSON.stringify(getSubArrays([1,2,3,4,5])));
I couldn't manage to get subarrays of an array with more than 23 items though.
Here are the performances. To be on the safe side i try with 22 items, first with recursive and then with binary route.
function getSubArrays(arr){
if (arr.length === 1) return [arr];
else {
subarr = getSubArrays(arr.slice(1));
return subarr.concat(subarr.map(e => e.concat(arr[0])), [[arr[0]]]);
}
}
var aminos = Array(22).fill().map((_,i) => i+1),
subarrs = [],
ps = 0,
pe = 0;
ps = performance.now();
subarrs = getSubArrays(aminos);
pe = performance.now();
console.log("recursive route took: " + (pe-ps) + "msec to produce " + subarrs.length + " sub arrays");
function getSubArrays(arr){
var len = arr.length,
subs = Array(Math.pow(2,len)).fill();
return subs.map((_,i) => { var j = -1,
k = i,
res = [];
while (++j < len ) {
k & 1 && res.push(arr[j]);
k = k >> 1;
}
return res;
}).slice(1);
}
var aminos = Array(22).fill().map((_,i) => i+1),
subarrs = [],
ps = 0,
pe = 0;
ps = performance.now();
subarrs = getSubArrays(aminos);
pe = performance.now();
console.log("binary route took: " + (pe-ps) + "msec to produce " + subarrs.length + " sub arrays");
This is fairly simple to do: https://jsfiddle.net/j1LuvxLq/
All you do is iterate possible lenghts and starting points and just print out the subsets. Complexity is O(n²) where n is the length of the original array. No way to improve it thought because that's the order of how many subsets there are.
var set = [3, 3, 9, 9, 5].join('')
var set_length = set.length
var subsets = []
for (var length_of_subset = 1; length_of_subset <= set_length; length_of_subset++) {
for (var start_of_subset = 0; start_of_subset <= set_length - length_of_subset; start_of_subset++) {
var current_subset = set.substring(start_of_subset, start_of_subset + length_of_subset)
if(subsets.indexOf(current_subset) == -1) {
subsets.push(current_subset.split(''))
}
}
}
// print the subsets out
for (s in subsets) {
$('body').append(subsets[s].join(', ') + '<br>')
}
Alternative, possibly nicer solution would be to use dynamic programming. Start with 3 and either remove last element or add next element. Check it out here: https://jsfiddle.net/p82fcs4m/
var set = [3, 3, 9, 9, 5].join('')
var subsets = []
take(set[0], set.substring(1))
function take(chosen, left) {
if(subsets.indexOf(chosen) != -1) {
return
}
subsets.push(chosen)
if (chosen.length > 1) {
take(chosen.substring(1), left)
}
if (left.length > 0) {
take(chosen.concat(left[0]), left.substring(1))
}
}
$('body').append(subsets.join('<br>'))
Try this:
function getSubArrays(arr) {
const subArrays = [];
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length - i; j++) {
const subArray = arr.slice(i, j + i + 1);
subArrays.push(subArray);
}
}
}
Still O(n²) tho
I believe using Array.slice is the most clean way to do it, isn't it?
function getSubArrays(arr) {
const subArrays = [];
for (var i = 0; i < arr.length; i++) {
for (var j = i; j < arr.length; j++) {
subArrays.push(arr.slice(i, j + 1));
}
}
return subArrays;
}

Simple histogram algorithm in Javascript

Im creating a histogram algorithm. Im following the solution offered here.
I want to simply count the number of times each value has occurred.
However I cant quite get the algorithm right. My code is:
var values = [2, 4, 6, 3, 3];
var val_max = 6;
var val_min = 2;
var num_bins = parseInt(val_max - val_min + 1);
console.log('num_bins is ', num_bins);
var bin_width = (val_max-val_min)/num_bins;
console.log('bin_width is ', bin_width);
var to_plot = [];
for (var i = 0; i < num_bins; i++) {
to_plot.push(0);
}
for (var x = 0; x < values.length; x++) {
var bin_idx = parseInt((values[x] - val_min) / bin_width);
to_plot[bin_idx] = to_plot[bin_idx] + 1;
}
console.log('to_plot is ', to_plot);
If you look at the console logs, you'll see:
to_plot is [1, 2, 1, 0, 0, NaN]
I want that last index to be "1". But the problem is for values close the the maximum value, bin_idx is out of range. How can I tweak this so that I would get the following results?
to_plot is [1, 2, 1, 0, 1]
The jsfiddle is here.
Here's what I would do:
const data = [2, 4, 6, 3, 3];
print(histogram(data, 1)); // [1, 2, 1, 0, 1]
print(histogram(data, 2)); // [3, 1, 1]
print(histogram(data, 3)); // [4, 1]
print(histogram(data, 4)); // [4, 1]
print(histogram(data, 5)); // [5]
function histogram(data, size) {
let min = Infinity;
let max = -Infinity;
for (const item of data) {
if (item < min) min = item;
else if (item > max) max = item;
}
const bins = Math.ceil((max - min + 1) / size);
const histogram = new Array(bins).fill(0);
for (const item of data) {
histogram[Math.floor((item - min) / size)]++;
}
return histogram;
}
function print(x) {
console.log(JSON.stringify(x));
}
This works for non-integer values too.
I think your bin_width is wrong. Try this calculation instead:
var bin_width = (val_max - val_min) / (num_bins - 1);
That makes the bin_width == 1 which lets the rest of your code work.
Since the number of bins is equal to the number of integers between val_min and val_max, the bin_width is 1, not 0.8 as currently being calculated. You're basically counting integers here. Use this loop to generate the histogram:
for (var x = 0; x < values.length; x++) {
to_plot[values[x] - val_min] ++;
}

Better way to turn a one-dimensional array into multi-dimensional array by required group size in JS

I am rather new to JS and I was working on a problem that asked to split an array (first argument) into groups the length of size (second argument) and returns them as a multidimensional array.
I got the problem to work right for all test cases but it suggested using the array `push()` method. I tried it multiple times and couldn't ever get it to work right. I think I was getting messed up with arrays being by reference. I eventually declared a new Array for each element. I went with a more classic deep copy each element at a time. I Didn't go back and try the `push()` method again. There has to be a more efficient way to do this. I want to write good code. Would love to see better versions please.
Thanks!
function chunk(arr, size) {
var group = 0;
var counter = 0;
var even = false;
var odd = false;
if (arr.length % size === 0) {
group = arr.length / size;
even = true;
} else {
group = Math.ceil(arr.length / size);
odd = true;
}
var newArr = new Array(group);
for (var i = 0; i < group; i++) {
newArr[i] = new Array(size);
}
for (i = 0; i < group; i++) {
for (var j = 0; j < size && counter < arr.length; j++) {
newArr[i][j] = arr[counter++];
}
}
return newArr;
}
chunk(['a', 'b', 'c', 'd'], 2);
Using Array.prototype.slice, the function can be written in a shorter way:
function chunk(array, size) {
var result = []
for (var i=0;i<array.length;i+=size)
result.push( array.slice(i,i+size) )
return result
}
You can try the slice method from the Array object. Here's an idea on how to use it.
var arr = [1, 2, 3, 4, 5, 6];
var newArr = [];
newArr.push(arr.slice(0, arr.length / 2));
newArr.push(arr.length / 2, arr.length);
This is just an shallow implementation but you can use the same concept inside a better written function.
Here's an example function:
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
function toChunks(arr, size) {
var i = 0,
chunks = [];
for (; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size););
}
return chunks;
}
toChunks(arr, 2);

Split an array by its index

Suppose I have an array:
var ay=[0,1,2,3,4,5,6,7,8,9];
Now I want to get two array:
var ay1=[0,2,4,6,8];
var ay2=[1,3,5,7,9];
What is efficient way?
Update:
I know the simple loop and modulo operator method(as elclanrs said) like this:
var ay1=[],ay2=[];
for(var i=0,len=ay.length;i++){
if(i%2==0){
ay2.push(ay[i]);
} else
ay1.push(ay[i]);
}
But I just wonder if there is any other efficient or cool way I do not know yet.
That is why I ask this simple question. I am not asking how to do , I am asking how to do better if possible!
So I do not think this post deserved the down-votes.
Let's say we generalize this problem a bit. Instead of just splitting an array's alternating elements into two arrays, why not allow for the array to be split in the same way into three, four, or more individual arrays?
It turns out it's about as easy to allow for any number of arrays as it is to do just two.
Think of the array like a rope made up of strands, and whatever number of strands you have in the rope, you want to unravel it. You could do it like this:
// "Unravel" an array as if it were a rope made up of strands, going
// around the rope and pulling off part of each strand one by one.
// 'rope' is the array and 'count' is the number of strands.
// Return an array of arrays, where the outer array has length 'count'
// and the inner arrays represent the individual strands.
function unravel( rope, count ) {
// Create each strand
var strands = [];
for( var i = 0; i < count; i++ ) {
strands.push( [] );
}
// Unravel the rope into the individual strands
for( var i = 0, n = rope.length; i < n; i++ ) {
strands[ i % count ].push( rope[i] );
}
return strands;
}
var rope = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
var s = unravel( rope, 2 );
console.log( s[0], s[1] );
var s = unravel( rope, 3 );
console.log( s[0], s[1], s[2] );
var s = unravel( rope, 5 );
console.log( s[0], s[1], s[2], s[3], s[4] );
This logs:
[0, 2, 4, 6, 8] [1, 3, 5, 7, 9]
[0, 3, 6, 9] [1, 4, 7] [2, 5, 8]
[0, 5] [1, 6] [2, 7] [3, 8] [4, 9]
Note that in the second case (count=3) one of the strands is longer than the other two—which is to be expected since 10 is not evenly divisible by 3.
Why not use a modulus function?
for (var i = 0; i < ay.length; i++) {
if (i%2 == 0)
{
ay1[i] = ay[i];
}
else
{
ay2[i] - ay[i];
}
}
var ay=[0,1,2,3,4,5,6,7,8,9];
var ay1 = [];
var ay2 = [];
for (var i = 0; i < ay.length; i++)
if (i % 2) ay2.push(ay[i]);
else ay1.push(ay[i]);
console.log(ay1, ay2);
http://jsfiddle.net/MPAAC/
var ay1=new Array();
var ay2=new Array();
for (var i = 0, len = ay.length; i < len; i++) {
//Check the i is odd or even
//insert any one of the array
}
Why not use the array's filter method?
var ay = [0,1,2,3,4,5,6,7,8,9];
var odds = ay.filter(function(val){ return val % 2 === 1; });
var evens = ay.filter(function(val){ return val % 2 === 0; });
With a shim from the above link being available if you need to support IE8
Here's one way:
var ay = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var ay1 = [];
var ay2 = [];
for(var i = 0; i < ay.length; i++) {
if(i % 2 == 0) {
ay1.push(ay[i]);
}else{
ay2.push(ay[i]);
}
}
funciton isEven(x) {
return x % 2 == 0;
}
function isOdd(x) {
return ! isEven(x);
}
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
arrEven = arr.filter(isEven),
arrOdd = arr.filter(isOdd);
Here is a variant, just because I can. Is it "the most efficient"? Heck no; but it has the same bounds - O(1) for a constant-sized number of result lists.
It's cool and super flexible - it's really just a "partition" variant that can unzip to n (possibly non-distinct) sequences. Most other answers given are all based on a specialized partition implementation that doesn't utilize a HoF, so I consider this superior in that aspect ;-) It should be a good exercise to work through how this works.
function unzip(arr) {
var conds = Array.prototype.slice.call(arguments, 1);
// Requires ES5 or a shim for `Array.map`.
var res = conds.map(function () { return [] });
for (var i = 0; i < arr.length; i++) {
for (var k = 0; k < conds.length; k++) {
if (conds[k](i, arr[i])) {
res[k].push(arr[i]);
}
}
}
return res;
}
r = unzip([0,1,2,3,4],
function (i) { return !(i % 2) }, // even
function (i) { return i % 2 }); // odd
alert(r[0] + " || " + r[1]);
If underscore.js is already being used (why not?) then a groupBy approach can be used.

Categories