Javascript Array Difference with Different Initialization Method - javascript

Can someone explain to me why the following happens in Javascript Console (Node 7.2.0):
Array in example I has different behavior than example II and III
EXAMPLE I
> var x = new Array(3).fill(new Array(2).fill(0))
> x
[ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ]
> x[0][0] = 1;
> x
[ [ 1, 0 ], [ 1, 0 ], [ 1, 0 ] ]
EXAMPLE II
> var y = [...new Array(3)].map(()=>{return [...new Array(2)].map(()=>0)})
> y
> [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ]
> y[0][0] = 1
> [ [ 1, 0 ], [ 0, 0 ], [ 0, 0 ] ]
EXAMPLE III
> var y = []
> y.push([ 0, 0 ])
> y.push([ 0, 0 ])
> y.push([ 0, 0 ])
> y
> [ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ]
> y[0][0] = 1
> [ [ 1, 0 ], [ 0, 0 ], [ 0, 0 ] ]
It seems that different ways to initialize array will cause different behaviors of an array. I'm confused and thank you in advance.

array.fill() returns the modified array, so you're filling an array with multiple references to the same array. That is why when you modify it in one place, it automatically shows up in the other places.
The first example is equivalent to doing something like:
var arr = [ 0, 0 ];
var x = [ arr, arr, arr ];

The reason for the difference is that in JS, objects (including arrays) are not copied, they are linked. In example I, you fill the array with a single array.
> var x = new Array(3).fill(new Array(2).fill(0))
// You have filled x with three links to the same array
> x
[ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ]
> x[0][0] = 1;
// You have now modified the actual array, this change is reflected in all the links to it.
> x
[ [ 1, 0 ], [ 1, 0 ], [ 1, 0 ] ]
What you are doing is the same as doing:
var a = [ 0, 0 ]
var x = [ a, a, a ]
OR
var a = [ 0, 0 ]
var x = []
x.push(a)
x.push(a)
x.push(a)
BTW, using new Array() is generally a bad practice. There is no benefit over the array literal syntax. Also, using new Array(n) makes "empty slots" in your array, which is very odd and can cause problems in your programs if you don't fill all the slots.

Related

Compare 2 arrays and combine in Javascript

How can i compare and combine 2 arrays using array functions of javascript or using lodash?
I have this initial array of dates for last 30 days.
[
'2022-12-11', '2022-12-12', '2022-12-13',
'2022-12-14', '2022-12-15', '2022-12-16',
'2022-12-17', '2022-12-18', '2022-12-19',
'2022-12-20', '2022-12-21', '2022-12-22',
'2022-12-23', '2022-12-24', '2022-12-25',
'2022-12-26', '2022-12-27', '2022-12-28',
'2022-12-29', '2022-12-30', '2022-12-31',
'2023-01-01', '2023-01-02', '2023-01-03',
'2023-01-04', '2023-01-05', '2023-01-06',
'2023-01-07', '2023-01-08', '2023-01-09',
'2023-01-10', '2023-01-11'
]
Then this is the second with count value.
[ [ '2023-01-09', 1 ], [ '2023-01-10', 3 ] ]
Now i have this code that compare and combine these array manually
let testData = [];
let k = 0;
dayList.forEach(o => {
let is_match = 0;
let frags = [];
submitted.forEach(i => {
if(o == i[0]){
is_match = 1;
frags = i;
}
});
testData[k] = [
(is_match == 1) ? frags[0] : o,
(is_match == 1) ? frags[1] : 0
];
k++;
});
console.log(testData);
this will result to...
[
[ '2022-12-11', 0 ], [ '2022-12-12', 0 ],
[ '2022-12-13', 0 ], [ '2022-12-14', 0 ],
[ '2022-12-15', 0 ], [ '2022-12-16', 0 ],
[ '2022-12-17', 0 ], [ '2022-12-18', 0 ],
[ '2022-12-19', 0 ], [ '2022-12-20', 0 ],
[ '2022-12-21', 0 ], [ '2022-12-22', 0 ],
[ '2022-12-23', 0 ], [ '2022-12-24', 0 ],
[ '2022-12-25', 0 ], [ '2022-12-26', 0 ],
[ '2022-12-27', 0 ], [ '2022-12-28', 0 ],
[ '2022-12-29', 0 ], [ '2022-12-30', 0 ],
[ '2022-12-31', 0 ], [ '2023-01-01', 0 ],
[ '2023-01-02', 0 ], [ '2023-01-03', 0 ],
[ '2023-01-04', 0 ], [ '2023-01-05', 0 ],
[ '2023-01-06', 0 ], [ '2023-01-07', 0 ],
[ '2023-01-08', 0 ], [ '2023-01-09', 1 ],
[ '2023-01-10', 3 ], [ '2023-01-11', 0 ]
]
As you can see the date 2023-01-09 and 2023-01-10 have values then the rest has 0 values.
Which is what i expected, i'm just new in coding a pure javascript application, i just translated my PHP code to javascript.
Now is there a way that this code may be simplified using array functions of javascript or using lodash?
Here's an approach, we first create a map with the date as key and count as value and then use this map to generate the result
const dates=["2022-12-11","2022-12-12","2022-12-13","2022-12-14","2022-12-15","2022-12-16","2022-12-17","2022-12-18","2022-12-19","2022-12-20","2022-12-21","2022-12-22","2022-12-23","2022-12-24","2022-12-25","2022-12-26","2022-12-27","2022-12-28","2022-12-29","2022-12-30","2022-12-31","2023-01-01","2023-01-02","2023-01-03","2023-01-04","2023-01-05","2023-01-06","2023-01-07","2023-01-08","2023-01-09","2023-01-10","2023-01-11",];
const count=[["2023-01-09",1],["2023-01-10",3]];
const countMap = count.reduce((acc, [date, count]) => {
acc[date] = count;
return acc;
}, {});
const result = dates.map((date) => [date, countMap[date] || 0]);
console.log(result)
You can simply run any loop and find the index of the current element in the submitted array and check if exist then assign date otherwise assign 0 to the array
var dayList = ['2022-12-11', '2023-01-10', '2023-01-09']
var submitted = [ [ '2023-01-09', 1 ], [ '2023-01-10', 3 ] ]
var testData = []
dayList.filter(o => {
const exist = submitted.find(e => e.indexOf(o) != -1)
if(exist){
testData.push([o, exist[1]])
} else {
testData.push([o, 0])
}
});
console.log("your data=", testData)

how to get the first item in nested, filtered array in javascirpt

I've got an array:
const aContracts = [[ '0x8ae127d224094cb1b27e1b28a472e588cbcc7620', 0 ],
[ '0xcbf4ab00b6aa19b4d5d29c7c3508b393a1c01fe3', 0 ],
[ '0x03cd191f589d12b0582a99808cf19851e468e6b5', 0 ],
[ '0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a', 1 ]]
And I'm trying to filter using the second item in each sub-array. So I used:
const aGoodContracts = aContracts.filter(contracts => contracts[1]===1);
And I get:
[["0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a", 1]]
However, I want [EDIT I want an array of all the "good" contracts]:
"0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a"
So I tried a solution I found here:
const aGoodContracts = aContracts.map(k => k.filter(contracts => contracts[1]===1));
But I get:
[[], [], [], []]
What am I missing?
Just map() your result to pick out the first element in the array. This will leave you with an array, and if you just want the first value (like your expected output) grab the zero index element.
const aContracts = [[ '0x8ae127d224094cb1b27e1b28a472e588cbcc7620', 0 ],
[ '0xcbf4ab00b6aa19b4d5d29c7c3508b393a1c01fe3', 0 ],
[ '0x03cd191f589d12b0582a99808cf19851e468e6b5', 0 ],
[ '0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a', 1 ]]
const aGoodContracts = aContracts.filter(contracts => contracts[1]===1).map(c => c[0]);
console.log(aGoodContracts);
console.log(aGoodContracts[0]);
You can try this
const aContracts = [[ '0x8ae127d224094cb1b27e1b28a472e588cbcc7620', 0 ],
[ '0xcbf4ab00b6aa19b4d5d29c7c3508b393a1c01fe3', 0 ],
[ '0x03cd191f589d12b0582a99808cf19851e468e6b5', 0 ],
[ '0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a', 1 ]]
const aGoodContracts = aContracts.filter(contracts => contracts[1]===1)[0]
console.log(aGoodContracts[0])

Javascript - Is there a more efficient way to create an array of arrays? - Examples provided

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

How to remove comma from my array value?

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 ] ]

Cartesian product without duplicates

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.

Categories

Resources