Need some help please - am using the excellent response to this histogram bucketing question as per code below. My question is what is the most elegant way to get my buckets with zero counts represented in the function output?
Current Output is:
[ [ '0', 2 ],
[ '32', 1 ],
[ '64', 2 ],
[ '80', 1 ],
[ '96', 1 ],
[ '112', 1 ] ]
which omits to note buckets 16 and 48 which have zero counts.
My desired output is:
[ [ '0', 2 ],
[ '16', 0 ],
[ '32', 1 ],
[ '48', 0 ],
[ '64', 2 ],
[ '80', 1 ],
[ '96', 1 ],
[ '112', 1 ] ]
All help much appreciated.
function defArrs (){
var arr = [1,16,38,65,78,94,105,124]
var binsize = 16;
var check = Object.entries(frequencies (arr,binsize));
console.log(check);
}
function frequencies(values, binsize) {
var mapped = values.map(function(val) {
return Math.ceil(val / binsize) -1;
});
console.log(mapped);
return mapped.reduce(function (freqs, val, i) {
var bin = (binsize * val);
freqs[bin] ? freqs[bin]++ : freqs[bin] = 1;
return freqs;
}, {});
}
Instead of starting with the empty object for .reduce:
}, {});
// ^^
construct one with all the properties you'll want to be included at the end (starting with a count of 0, of course).
const initialObj = Object.fromEntries(
Array.from(
{ length: 7 },
(_, i) => [i * binsize, 0]
)
);
And pass that as the second parameter to .reduce.
That approach also means that this
freqs[bin] ? freqs[bin]++ : freqs[bin] = 1;
will simplify to
freqs[bin]++;
Or, even better:
const frequencies = (nums, binsize) => {
const mapped = nums.map(num => Math.ceil(num / binsize) - 1;
const grouped = Object.fromEntries(
{ length: 7 },
(_, i) => [i * binsize, 0]
);
for (const val of mapped) {
grouped[binsize * val]++;
}
return grouped;
};
I am trying to add a copy of an array to another array with array.slice(), but when I update the original, it updates the copy that I added. How can I add a copy that isn't altered when the original is altered?
I've tried using result.unshift[...curRow], and result.unshift(curRow.slice()) when I add
function game(n) {
var result = [];
let row1=[[1, 2]];
for (var i=1;i<n;i++){
var cur = row1[i];
var prev = row1[i - 1];
row1.push([prev[0] + 1, prev[1] + 1]);
}
result.push(row1.slice());
let curRow = row1.slice();
for (var i=1;i<n;i++){
for (var j = 0; j<curRow.length ;j++){
curRow[j][1]++;
}
result.unshift(curRow.slice());
console.log('curRow =',curRow);
console.log('result = ', result)
}
console.log('result = ', result)
return result
}
game(3);
This is my current output:
'result ='[ [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ] ] ]
'result = '[ [ [ 1, 3 ], [ 2, 4 ], [ 3, 5 ] ], [ [ 1, 3 ], [ 2, 4 ], [ 3, 5 ] ] ]
'result = '[ [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ], [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ], [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ] ]
I want the result array to contain each iteration of the curRow array, instead of just having copies of the latest one.
In JavaScript, objects/arrays, also known as non-primitive types, are given a reference, rather than value. Therefore, this particular reference points to the object's location in the memory. That variable you are working with has a 'reference' rather than containing an actual 'value'. This is why it is mutated when you use it on the game() function.
To work with that, you should create a shallow copy of that array, before making any manipulation/mutation to it.
const copy = originalArray.map(element => ({...element}));
Now, all changes to the copy array will not be applied to originalArray.
Because in Javascript Arrays are handled by reference, when you reasign an array to a new variable, the new variable only holds a reference to the original array. Any alteration on the original array will be reflected in any of its instances/references (beucause they are essentially the same object).
Since the ES6 version of the standard we have Array.from() to perform copies or duplicates of the contents of an Array.
It's my favorite way to go, is fast, safe and readable.
The Array.from() method creates a new, shallow-copied Array instance from an array-like or iterable object. Extended documentation here
Check the example :
let a = [1,2,3,4,5];
let b = Array.from(a);
// modify first array to test
a[0] = 666;
// output second array to check
console.log(b)
// [1,2,3,4,5] OK!
you can use this concept:
firstArray=firstArray.concat(...secondArray);
Question: Is there a more efficient way of creating an array of arrays of incrementing numbers?
I've created a function to produce an array of arrays of incrementing numbers, which took far longer than expected, and I'm sure there is a more efficient way to achieve this (I'm new to JS).
Note for the genArray function of both example 1 and 2:
argu1 declares the start of the number range (e.g. 0 = start from 0),
argu2 declares the end of the number range (e.g. 9 = end at 9),
argu3 declares how many numbers are needed in each individual array (e.g. 3 = generate 3 numbers in the array),
argu4 carries the temp array to generate a single array of numbers,
argu5 carries the array of arrays through the function and nested functions.
Example 1: Below is the code purely for creating an array of arrays of incrementing numbers. My question refers to making a more efficient version of this function.
function genArray(start, finish, quantity, array, allArray = []) {
var collectArray = allArray;
//Cycle through digits from start to finish, e.g. 0-9
for (var i = start; i <= finish; i++) {
var tempArray = [];
//Collect digits for a single array if not first iteration
if (array !== undefined) {
tempArray = tempArray.concat(array);
};
//Add digit to single level array
tempArray.push(i);
//If not highest level, go higher
if (quantity > 1) {
var genArray2 = genArray(start, finish, quantity-1, tempArray, collectArray);
}
//If highest level collect a single array
else if (quantity == 1) {
collectArray.push(tempArray);
}
}
return collectArray;
}
//Call function with arguments
//argu1 declares the start of the number range, argu2 declares the end of the number range, argu3 declares how many numbers are needed in each individual array, argu4 carrays the temp array to generate a single array of numbers, argu4 carrys the array of arrays throught the function and nested functions.
var genArray2 = genArray(0, 9, 3);
console.log(genArray2);
This produces a log like so:
[ [ 0, 0, 0 ],
[ 0, 0, 1 ],
[ 0, 0, 2 ],
[ 0, 0, 3 ],
[ 0, 0, 4 ],
[ 0, 0, 5 ],
[ 0, 0, 6 ],
[ 0, 0, 7 ],
[ 0, 0, 8 ],
[ 0, 0, 9 ],
[ 0, 1, 0 ],
[ 0, 1, 1 ],
[ 0, 1, 2 ],
[ 0, 1, 3 ],
[ 0, 1, 4 ],
[ 0, 1, 5 ],
[ 0, 1, 6 ],
[ 0, 1, 7 ],
[ 0, 1, 8 ],
[ 0, 1, 9 ],
[ 0, 2, 0 ],
[ 0, 2, 1 ],
[ 0, 2, 2 ],
[ 0, 2, 3 ],
[ 0, 2, 4 ],
[ 0, 2, 5 ],
[ 0, 2, 6 ],
[ 0, 2, 7 ],
[ 0, 2, 8 ],
[ 0, 2, 9 ],
[ 0, 3, 0 ],
[ 0, 3, 1 ],
[ 0, 3, 2 ],
[ 0, 3, 3 ],
[ 0, 3, 4 ],
.... up to [ 9, 9, 9 ]
Example 2: Below is the code I'm actually using, with the only change being the addition of a check to see if an array produced is ascending and each number is unique, and storing only those that are true in both cases. Providing this for context and in case it's useful to someone:
//Check if ascending
function ascending(x) {
return x == parseInt(x.toString().split('').sort().join(''));
}
//Check if unique
function unique(x) {
return x.toString().split('').length == [...new Set(x)].length
}
//Create an array of arrays of ascending and unique numbers
function genArray(start, finish, quantity, array, allArray = []) {
var collectArray = allArray;
//Cycle through digits from start to finish, e.g. 0-9
for (var i = start; i <= finish; i++) {
var tempArray = [];
//Collect digits for a single array if not first iteration
if (array !== undefined) {
tempArray = tempArray.concat(array);
};
//Add digit to single level array
tempArray.push(i);
//If not highest level, go higher
if (quantity > 1) {
var genArray2 = genArray(start, finish, quantity-1, tempArray, collectArray);
}
//If highest level collect a single array
else if (quantity == 1 && ascending(tempArray.join('')) && unique(tempArray.join(''))) {
collectArray.push(tempArray);
}
}
return collectArray;
}
//Call function with arguments
var genArray2 = genArray(0, 9, 3);
console.log(genArray2);
This produces a log like so:
[ [ 0, 1, 2 ],
[ 0, 1, 3 ],
[ 0, 1, 4 ],
[ 0, 1, 5 ],
[ 0, 1, 6 ],
[ 0, 1, 7 ],
[ 0, 1, 8 ],
[ 0, 1, 9 ],
[ 0, 2, 3 ],
[ 0, 2, 4 ],
[ 0, 2, 5 ],
[ 0, 2, 6 ],
[ 0, 2, 7 ],
[ 0, 2, 8 ],
[ 0, 2, 9 ],
[ 0, 3, 4 ],
[ 0, 3, 5 ],
[ 0, 3, 6 ],
[ 0, 3, 7 ],
[ 0, 3, 8 ],
[ 0, 3, 9 ],
[ 0, 4, 5 ],
[ 0, 4, 6 ],
[ 0, 4, 7 ],
[ 0, 4, 8 ],
[ 0, 4, 9 ],
[ 0, 5, 6 ],
[ 0, 5, 7 ],
[ 0, 5, 8 ],
[ 0, 5, 9 ],
[ 0, 6, 7 ],
[ 0, 6, 8 ],
[ 0, 6, 9 ],
[ 0, 7, 8 ],
[ 0, 7, 9 ],
[ 0, 8, 9 ],
[ 1, 2, 3 ],
[ 1, 2, 4 ],
[ 1, 2, 5 ],
[ 1, 2, 6 ],
.... up to [ 7, 8, 9 ]
Without recursion you will be able to speed it up. Here is a loop that just uses the previously added subarray to calculate the next. It uses the mechanism one has when adding a 1 to a decimal number: first increment the right most digit. If it goes out of range (in decimal: it becomes 10), then set it back to the lowest digit and increment the digit on its left, ...etc, until the last changed digit remains within range:
function genArray(start, finish, quantity) {
const current = Array(quantity).fill(start);
const result = [];
for (let i = quantity; i >= 0; null) {
result.push(current.slice());
for (i = quantity; i--; null) {
current[i]++;
if (current[i] <= finish) break;
current[i] = start;
}
}
return result;
}
console.log(genArray(0, 2, 3));
If you are willing to do a little math, there is a pretty quick easy way to do this in general terms. The basic insight is that a number like 768 can be broken down to various log10 components taken modulo 10. For example Math.floor(768/100) % 10 gets you the third digit. Math.floor(768/10) % 10 get you the second. To get the length of the inner arrays you need you can take Math.floor(Math.log10(largestNumber + 1)). So for 1000 this will be 4, for 999 it will be 3, etc. The only annoying part of this arrangement it that arrays are build left to right but numbers are build right to left. Thats why we are working with length - index in the inner arrays.
You can put this together with Array.from to make a succinct function that avoids a lot of string parsing and if/else clauses:
function genArray(start, finish) {
return Array.from({length: finish - start + 1}, (_, i) => {
let ind = i + start
let innerLength = Math.floor(Math.log10(finish + 1))
return Array.from({length: innerLength + 1}, (_, i) => Math.floor(ind / (10 ** (innerLength - i))) % 10)
})
}
let a = genArray(0, 20)
console.log(a.join(' · '))
a = genArray(1020, 1040)
console.log(a.join(' · '))
Also, it's not clear how large your arrays will be, but if you are working with large sets of numbers, it can be a little more memory efficient to make a generator so you only produce the inner arrays as needed. It's not right solution for everything, but since it's almost the same code, I thought I'd mention it:
function* genArray(start, finish) {
let innerLength = Math.floor(Math.log10(finish + 1))
while (start <= finish) {
yield Array.from({length: innerLength + 1}, (_, i) => Math.floor(start / (10 ** (innerLength - i))) % 10)
start++
}
}
let a = genArray(101, 105)
console.log(a.next().value)
let b = genArray(20, 30)
console.log([...b].join(' · '))
This is a possible solution:
function genArrays(start, end){
let max_len = String(end).length;
let arr = [];
let cur_num = start;
let arr_num;
for (let i = start; i <= end; i++){
str_num = String(cur_num).padStart(max_len, '0').split('').map(x => parseInt(x));
arr.push(str_num);
cur_num++;
}
return arr;
}
console.log(genArrays(0, 1000));
console.log(genArrays(102, 1043));
The core is here: str_num = String(cur_num).padStart(max_len, '0');. A counter is firstly stringed and then it is applied a padding on the left in order to reach the length of the stringed end.
I don't speak a perfect english, and I hope to understand your question.
From the example you provided, it seems you just need a 2 level array, the first one containing n arrays of incremental numbers.
Instead of using a recursive function, that use lot of memory, you can try to use a normal for cycle, and a split on every number, padded with 0s to get the n length.. Don't know if it could work for you.
0002.split create an array [0,0,0,2]..
and then push it on the main array
Should be the fastest way
//s start, e end, n array size
f=new Array();
a=new Array();
for (i=s; i<e; i++) {
t=pad(i,n);
a=i.toString().split('');
f[f.length]=a;
}
function pad(s,n) {
while (s.length < n)
s = '0' + s;
return s;
};
Cheers
Daniele
hi pretty new to javascript.I am pretty confused in spliting my array value
console.log(arr)//[ [ [ 10, 0 ] ], [ [ 8, 0 ] ], [ [ 8, 0 ] ], [ [ 5, 2 ] ] ]
var line = "";
arr.forEach(e => {
e.forEach(f => line += "[" + f.join(",") + "],");
});
console.log(line);//[10,0],[8,0],[8,0],[5,2],
But i want my ouptput like this to do matrix addition
console.log(line);//[[10,0],[8,0],[8,0],[5,2]]
You can use map() for this.
var arr = [ [ [ 10, 0 ] ], [ [ 8, 0 ] ], [ [ 8, 0 ] ], [ [ 5, 2 ] ] ];
var result = arr.map(function(a) {
return a[0];
});
console.log(result)
You could do this by changing where the join happens and pre/app-ending some square brackets, e.g.
var line = arr.map(e => e.map(f => f.join(",")));
console.log('[' + line.join('],[') + ']');
// [10,0],[8,0],[8,0],[5,2]
I do have to ask though, why are you getting back a set of arrays each with a single value? Is it possible to avoid getting a dataset like that in the first place? You could avoid the double map/foreach that way.
For instance if you had one level less nesting in your source array the map line would become a little simpler
var arr = [ [ 10, 0 ], [ 8, 0 ], [ 8, 0 ], [ 5, 2 ] ];
var line = arr.map(f => f.join(","));
console.log('[' + line.join('],[') + ']');
This is of course if you want to specifically output the string for the array matrix, if you just wanted a flatter version of your original array then you could do:
var newList = arr.map(f => f[0]);
// [ [ 10, 0 ], [ 8, 0 ], [ 8, 0 ], [ 5, 2 ] ]
I am using a cartesian product function that given [1], [1,2,3], [1,2,3] returns 9 combinations:
[ [ 1, 1, 1 ],
[ 1, 2, 1 ],
[ 1, 3, 1 ],
[ 1, 1, 2 ],
[ 1, 2, 2 ],
[ 1, 3, 2 ],
[ 1, 1, 3 ],
[ 1, 2, 3 ],
[ 1, 3, 3 ] ]
But I need to remove those with the same items regardless of the order, so [ 1, 3, 1 ] and [ 1, 1, 3 ] are the same to me. The result should contain 6 items:
[ [ 1, 1, 1 ],
[ 1, 2, 1 ],
[ 1, 3, 1 ],
[ 1, 2, 2 ],
[ 1, 3, 2 ],
[ 1, 3, 3 ] ]
I can write a function that compares all possible pairs with _.xor, but for larger numbers it will probably be very inefficient. Is there a good way in Javascript to do this? An efficient way to compare all possible pairs or an algorithm for cartesian product without duplicates?
sort each array of the cartesian product
[ 1, 2, 1 ] -> [1 , 1 , 2]
[ 1, 1, 2 ] -> [1 , 1 , 2]
then gather these sorted arrays into a set, that will remove the duplicates.
Of course, you can do that while constructing the cartesian product rather than afterward.
JavaScript has Set and Map, however they compare objects and arrays by reference rather than by value, so you cannot take advantage of it directly. The idea is to use a key function which sorts and json encodes the items before putting it in a set.
pure ES5:
function product(sets) {
if (sets.length > 0) {
var head = sets[0];
var tail = product(sets.slice(1));
var result = [];
head.forEach(function(x) {
tail.forEach(function(xs) {
var item = xs.slice(0);
item.unshift(x);
result.push(item);
});
});
return result;
} else {
return [[]];
}
}
function myKeyFn(item) {
return JSON.stringify(item.slice(0).sort());
}
function uniqBy(items, keyFn) {
var hasOwn = Object.prototype.hasOwnProperty, keyset = {};
return items.filter(function(item) {
var key = keyFn(item);
if (hasOwn.call(keyset, key)) {
return false;
} else {
keyset[key] = 1;
return true;
}
});
}
function uniqProduct(sets) {
return uniqBy(product(sets), myKeyFn);
}
function log(x) {
console.log(x);
var pre = document.createElement('pre');
pre.appendChild(document.createTextNode(x));
document.body.appendChild(pre);
}
log(uniqProduct([[1],[1,2,3],[1,2,3]]).map(JSON.stringify).join("\n"));
<pre></pre>
lodash + modern JavaScript:
// Note: This doesn't compile on current babel.io/repl due to a bug
function product(sets) {
if (sets.length > 0) {
const [x, ...xs] = sets;
const products = product(xs);
return _.flatMap(x, head => products.map(tail => [head, ...tail]));
} else {
return [[]];
}
}
function uniqProduct(sets) {
return _.uniqBy(product(sets), x => JSON.stringify(x.slice(0).sort()));
}
console.log(uniqProduct([[1],[1,2,3],[1,2,3]]).map(JSON.stringify).join("\n"));
JavaScript has set data structure.
So store your results in a set where each element of the set is a collection of pairs of numbers from the original sets along with the number of times that number occurs.
So your result would look something like this:
[
{1:3},
{1:2, 2: 1},
{ 1:2, 3:1},
{ 1:1, 2:2},
{ 1:1, 2:1, 3:1},
{ 1:1, 3:2 } ]
This way, you won't be able to add the object a second time to the set.