Related
This question already has answers here:
Zip arrays in JavaScript?
(5 answers)
Closed 2 months ago.
I have two arrays. One contains a series of dates. The other array contains a series of data. What is the best way to combine the two arrays into one array of objects.
As an example the first element of the first array will be in the same object as the first element in the second array.
HERE ARE THE TWO ARRAYS
const datesArray = [
"2017-01-01",
"2017-01-02",
"2017-01-03",
"2017-01-04",
"2017-01-05",
"2017-01-06",
"2017-01-07",
"2017-01-08",
"2017-01-09",
"2017-01-10",
"2017-01-11",
"2017-01-12",
"2017-01-13",
"2017-01-14",
"2017-01-15",
"2017-01-16",
"2017-01-17",
"2017-01-18",
"2017-01-19",
"2017-01-20",
"2017-01-21",
"2017-01-22",
"2017-01-23",
"2017-01-24",
"2017-01-25",
"2017-01-26",
"2017-01-27",
"2017-01-28",
"2017-01-29",
"2017-01-30",
"2017-01-31",
];
const snowfallArray = [
0.98, 0, 0, 0, 0, 0, 0.35, 0.42, 0, 0.14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
HERE IS MY EXPECTED OUTPUT
let combinedArray = [{
date: *first element from dates array*,
snowFall: *first element from snowfall array*
},
{
date: *second element from dates array*,
snowFall: *second element from snowfall array*
}]
EXPECTED OUTPUT EX:
let combinedArray = [{date: '2017-01-01', snowFall: 0.98},{date: '2017-01-02', snowFall: 0}]
I do not even know where to begin..
Assuming the arrays are of equal lengths, then this should work:
const datesArray = [
"2017-01-01",
"2017-01-02",
"2017-01-03",
"2017-01-04",
"2017-01-05",
"2017-01-06",
"2017-01-07",
"2017-01-08",
"2017-01-09",
"2017-01-10",
"2017-01-11",
"2017-01-12",
"2017-01-13",
"2017-01-14",
"2017-01-15",
"2017-01-16",
"2017-01-17",
"2017-01-18",
"2017-01-19",
"2017-01-20",
"2017-01-21",
"2017-01-22",
"2017-01-23",
"2017-01-24",
"2017-01-25",
"2017-01-26",
"2017-01-27",
"2017-01-28",
"2017-01-29",
"2017-01-30",
"2017-01-31",
];
const snowfallArray = [
0.98, 0, 0, 0, 0, 0, 0.35, 0.42, 0, 0.14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
const combined = snowfallArray.map((snowfall, index) => ({snowfall, date: datesArray[index]}));
console.log(combined);
assuming your array are the same length:
const datesArray = [
"2017-01-01",
"2017-01-02",
"2017-01-03",
"2017-01-04",
"2017-01-05",
"2017-01-06",
"2017-01-07",
"2017-01-08",
"2017-01-09",
"2017-01-10",
"2017-01-11",
"2017-01-12",
"2017-01-13",
"2017-01-14",
"2017-01-15",
"2017-01-16",
"2017-01-17",
"2017-01-18",
"2017-01-19",
"2017-01-20",
"2017-01-21",
"2017-01-22",
"2017-01-23",
"2017-01-24",
"2017-01-25",
"2017-01-26",
"2017-01-27",
"2017-01-28",
"2017-01-29",
"2017-01-30",
"2017-01-31",
];
const snowfallArray = [
0.98, 0, 0, 0, 0, 0, 0.35, 0.42, 0, 0.14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
const results = [];
for (let i=0; i< snowfallArray.length; i++){
results.push({
date: datesArray[i],
snowFall: snowfallArray[i]
})
}
console.log(results);
I want to create an array of object like this :
array_1 :
array(0, 0, 0, 1, 0, 1, 0, 1, 1);
object_2 :
obj: [
{
id: 0,
fonction: 'hey'
},
{
id: 1,
fonction: 'hi'
}
]
So, I want the following output :
result :
obj: [
{
id: 0, // id of object_2
max: 5, // number of id value in array_1
value: 0, // add an empty value
fonction: 'hey' // fonction text in oject_2
},
{
id: 1,
max: 4,
value: 0,
fonction: 'hi'
}
]
Thanks for your help
You can just map your existing object, and count the ids inside your array using the filter + length option
const arr = [0, 0, 0, 1, 0, 1, 0, 1, 1];
const target = [
{
id: 0,
fonction: 'hey'
},
{
id: 1,
fonction: 'hi'
}
];
console.log( target.map( item => ({
...item,
max: arr.filter( v => item.id === v ).length,
value: 0
}) ) );
My goal here is to create an object, 'tallys,' that has a key for each user in a game, and the value assigned to those user keys is an array representing weekly dollar values won in a game. To begin, I assign each user in the game an array of length 26 (representing 26 weeks in the game) with 26 values of '0' to represent $0 values for each week to begin with via this code:
const maxWeek = 25;
let weeks = [];
for (let i=0; i < maxWeek+1; i++) {
weeks.push(0)
};
let tallys = {};
users.forEach(user => {
tallys[user] = weeks;
});
...which results in an object like so:
tallys is {
michaeljpow: [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0
],
'Mr. Cheeseburger': [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0
],
'brewster.stanislaw': [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0
],
... etc
}
Then, I run a little for loop to calculate the totals for each user by week ... with the 'j' variable here starting at week 1, and the loop running for as many weeks as the game has been running, like this:
for (let j=1; j<wk+1; j++) {
let sums = await BonusEvents().where({week: j}).sum('dollars_total as d').groupBy('username').select('username');
console.log('sums for week ', j, ' are ', sums);
};
I'm still in good shape at this point -- this gets me what I want, two different 'sums' arrays for the two weeks that have taken place in the game so far:
sums for week 1 are [
{ d: -4.27, username: 'Mr. Cheeseburger' },
{ d: -4.27, username: 'dannuzio' },
{ d: 11.29, username: 'james.johnsonf' },
{ d: -4.27, username: 'brewster.stanislaw' },
{ d: 16.47, username: 'eric.wj.clarke' },
{ d: -4.27, username: 'mikeduin' },
{ d: 11.29, username: 'BH84' },
{ d: -4.27, username: 'kevsims' },
{ d: 11.29, username: 'michaeljpow' },
{ d: 47.58, username: 'superbkn' },
{ d: -4.27, username: 'whpfwashington' },
{ d: -4.27, username: 'benjamininghram' }
]
sums for week 2 are [
{ d: -1.41, username: 'benjamininghram' },
{ d: -1.41, username: 'BH84' },
{ d: -1.41, username: 'brewster.stanislaw' },
{ d: -1.41, username: 'dannuzio' },
{ d: -1.41, username: 'eric.wj.clarke' },
{ d: -1.41, username: 'james.johnsonf' },
{ d: -1.41, username: 'kevsims' },
{ d: -1.41, username: 'michaeljpow' },
{ d: -1.41, username: 'mikeduin' },
{ d: -1.41, username: 'Mr. Cheeseburger' },
{ d: 14.06, username: 'superbkn' },
{ d: -1.41, username: 'whpfwashington' },
]
So here's where things get messy. I modify the previous function to do a forEach on my 'sums' array for each week, with designs on modifying the 'tallys' object to modify the array position assigned to each week with the appropriate dollar value for each user. (Also, lest the 'j-1' here look confusing -- that's because I want the game week of 1 assigned to the 0 position in the array rather than the first position):
for (let j=1; j<wk+1; j++) {
let sums = await BonusEvents().where({week: j}).sum('dollars_total as d').groupBy('username').select('username');
sums.forEach(weekSum => {
tallys[weekSum.username][j-1] = weekSum.d;
console.log('tallys are ', tallys);
})
};
So what I would EXPECT here is to end up with something like this:
tallys = {
mikeduin: [-4.27, -1.41, 0, 0, 0 ... etc],
superbkn: [47.58, 14.06, 0, 0, 0 ... etc],
... etc
}
... with the appropriate values for each user. HOWEVER! This is what I end up with as my tallys object once all the loops etc have finished:
tallys are {
michaeljpow: [
-4.27, -1.41, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0
],
'Mr. Cheeseburger': [
-4.27, -1.41, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0
],
superbkn: [
-4.27, -1.41, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0
],
...etc
}
.... etc, but for every single user. It's assigning EVERY user the exact same value over and over again -- they all end up with -4.27 and -1.41 just because that was the last 'd' value that got looped through.
Why is it updating every username with the last relevant values when I have tallys[weekSum.username] in my code, so that it should only be updating a single username each time in the tallys object??
Thanks in advance for any help!
EDIT: Thank you again for everyone's help. #Bergi's answer in the comments is correct. When I edit my initial code that creates 'tallys' as he suggests like so -- everything works as it should. #vothanhdat's approach that I marked as the suggested answer in which I clone the initial 'weeks' array also solves the problem.
let tallys = {};
users.forEach(user => {
let weeks = [];
for (let i=0; i < maxWeek+1; i++) {
weeks.push(0)
};
tallys[user] = weeks;
});
Your problem is every tallys[user] reference to the same weeks array.
Example:
let a = [1,2,3]
console.log(a) // [1,2,3]
let b = a // now a & b is reference to the same array
b[0] = 100
console.log(a) // [100,2,3]
So the solution is clone array week for every user
const maxWeek = 25;
let weeks = [];
for (let i = 0; i < maxWeek + 1; i++) {
weeks.push(0)
};
let tallys = {};
users.forEach(user => {
tallys[user] = [...weeks] //Clone week array
});
:) I'm creating a maze using JS and P5, with a two dimensional array filled with numbers 0-8. 0 are empty spots, 1 are walls, 2 is the character you walk with, 3 is the exit and 4-8 are items that randomly spawn. In order to exit the maze (through 3, which is set on a fixed spot), all items need to be collected (if you walk over an item, the value of this spot changes back to 0), so every value in the array should be below 4 in order to exit. Now I need a way to check if this is the case.
I tried it with every() but I guess this only works for regular arrays. I suppose I need a for loop but I don't know this should look. So that's where I need help!
My maze consists of 18 rows and columns, like so (but then 15 more rows)
let maze = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,2,0,0,0,0,1,0,1,0,0,0,0,1,0,0,1,0,1,0,0,0,3],
[1,1,1,1,1,0,1,0,0,0,1,1,0,0,0,0,1,0,1,0,1,0,1]
]
The items spawn randomly, this already works. Now I tried checking if every value is <= 3, with the every, like so
function checkBoard(mazenumbers){
return mazenumbers <= 3;
}
function alertMazenumbers() {
alert(maze.every(checkBoard));
}
And want this to display through an alert, once you walk into the exit location, like this
else if(direction === 'right') {
if(maze[playerPos.y][playerPos.x + 1] == 3) {
alertMazenumbers();
}
I want to get an alert with true if every value is <= 3, and false if not.
Currently, with this every(), I do get the alert but it only returns false, even when all items are cleared and it should return true.
You are on the right track using every!
The maze is an array of arrays (as Denys mentioned in his comment), so you have to use every twice, like so:
function canExitMaze(maze) {
return maze.every(row => row.every(cell => cell <= 3))
}
If you don't recognize the arrow function syntax (=>) this article explains it.
Hope this helps!
You can check if every point in the maze is <=3 by doing this
const isTrue = num => num <= 3; // is a single cell true
const isRowTrue = row => row.every(isTrue); // are all cells in a row true
const isMazeTrue = rows => rows.every(isTrue); // are all cells in all rows true
const maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 3],
[1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1]
];
console.log(isMazeTrue(maze));
Method 1: Check if every array only contains numbers that are <= 3
let maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 3],
[1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1]
];
function testMaze(maze) {
return maze.every(row => row.every(itemIsValid));
}
function itemIsValid(item) {
return item <= 3;
}
console.log(testMaze(maze));
maze[2][4] = 4;
console.log(testMaze(maze));
Method 2: Merge the arrays and search the numbers
var maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 3],
[1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1]
];
function testMaze(maze) {
return [].concat(...maze).every(itemIsValid);
}
function itemIsValid(item) {
return item <= 3;
}
console.log(testMaze(maze));
maze[2][4] = 4;
console.log(testMaze(maze));
Method 3: Convert the maze to a string and use regex
var maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 3],
[1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1]
];
function testMaze(maze) {
//or maze.toString().match(/\d+/g).every(x => itemIsValid(+x));
return !/[4-8]/g.test(`${maze}`);
}
function itemIsValid(item) {
return item <= 3;
}
console.log(testMaze(maze));
maze[2][4] = 4;
console.log(testMaze(maze));
I have the following array:
arr = [
[ 1, 1, 1, 1, 1, 1, 1 ],
[ 1, 1, 0, 0, 1, 1, 0 ],
[ 1, 0, 0, 0, 1, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 1, 0, 1, 0, 0, 2 ],
[ 1, 0, 0, 1, 0, 2, 4 ],
[ 0, 0, 0, 0, 2, 4, 4 ],
[ 0, 0, 0, 0, 4, 4, 0 ],
[ 1, 1, 1, 0, 0, 0, 0 ],
[ 1, 1, 0, 2, 0, 0, 2 ],
[ 1, 0, 0, 4, 0, 2, 0 ],
[ 0, 0, 0, 4, 2, 0, 0 ],
[ 0, 0, 2, 0, 0, 0, 1 ],
[ 0, 2, 4, 0, 0, 1, 2 ],
[ 2, 4, 4, 2, 1, 2, 4 ],
[ 4, 4, 0, 0, 2, 4, 0 ]
]
Currently, I'm getting the max array sum in arr i.e 19 like this
function getMaxSum(arr) {
return arr.map(e => e.reduce((a, b) => a + b, 0)).sort((a,b) => a - b)[arr.length - 1];
}
I need to know is there any better way to achieve this?
I'm using array length of the original array to get the last element of resulting array because in this case, length of original array and resulting array is same. If the scenario is different then how can I use the length of the resulting array here:
return arr.map(e => e.reduce((a, b) => a + b, 0)).sort((a,b) => a - b)[HERE - 1];
Not a huge improvement, but it seems a little more literal to spread the values into Math.max
const data = [
[ 1, 1, 1, 1, 1, 1, 1 ],
[ 1, 1, 0, 0, 1, 1, 0 ],
[ 1, 0, 0, 0, 1, 0, 0 ],
[ 0, 0, 0, 0, 0, 0, 0 ],
[ 0, 1, 0, 1, 0, 0, 2 ],
[ 1, 0, 0, 1, 0, 2, 4 ],
[ 0, 0, 0, 0, 2, 4, 4 ],
[ 0, 0, 0, 0, 4, 4, 0 ],
[ 1, 1, 1, 0, 0, 0, 0 ],
[ 1, 1, 0, 2, 0, 0, 2 ],
[ 1, 0, 0, 4, 0, 2, 0 ],
[ 0, 0, 0, 4, 2, 0, 0 ],
[ 0, 0, 2, 0, 0, 0, 1 ],
[ 0, 2, 4, 0, 0, 1, 2 ],
[ 2, 4, 4, 2, 1, 2, 4 ],
[ 4, 4, 0, 0, 2, 4, 0 ]
]
function getMaxSum(arr) {
return Math.max(...arr.map(e => e.reduce((a, b) => a + b, 0)))
}
console.log(getMaxSum(data))
As #Rajesh points out, Math.max is faster that a sort:
const numbers = Array(10000).fill().map((x,i)=>i);
const max = numbersIn => Math.max(...numbersIn);
const getMaxViaSort = numbersIn => numbersIn
.sort((a, b) => a > b ? -1 : 1)[0]
console.time('max');
max(numbers);
console.timeEnd('max');
console.time('max via sort');
getMaxViaSort(numbers);
console.timeEnd('max via sort');
The optimal strategy should be one where we need the least interaction with the arrays.
In my method i test the array products individually and compare that with a variable inside my loop. In this way i don't need to run a new implied loop to have Math.max check all my entries again, netting a speed boost.
This also saves on memory management as i don't need to map and return a new array of results for Math.max.
At the end of the loop i simply return the variable.
var data = [
[1, 1, 1, 1, 1, 1, 1],
[1, 1, 0, 0, 1, 1, 0],
[1, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 1, 0, 0, 2],
[1, 0, 0, 1, 0, 2, 4],
[0, 0, 0, 0, 2, 4, 4],
[0, 0, 0, 0, 4, 4, 0],
[1, 1, 1, 0, 0, 0, 0],
[1, 1, 0, 2, 0, 0, 2],
[1, 0, 0, 4, 0, 2, 0],
[0, 0, 0, 4, 2, 0, 0],
[0, 0, 2, 0, 0, 0, 1],
[0, 2, 4, 0, 0, 1, 2],
[2, 4, 4, 2, 1, 2, 4],
[4, 4, 0, 0, 2, 4, 0]
];
function getMaxSum1(arr) {
//The original method
return arr.map(function (e) { return e.reduce(function (a, b) { return a + b; }, 0); }).sort(function (a, b) { return a - b; })[arr.length - 1];
}
function getMaxSum2(arr) {
//From https://stackoverflow.com/a/51704254/5242739
return Math.max.apply(Math, arr.map(function (e) { return e.reduce(function (a, b) { return a + b; }, 0); }));
}
function sumArray(arr) {
var val = 0;
for (var index = 0; index < arr.length; index++) {
val += val;
}
return val;
}
function getMaxSum3(arr) {
//My method
var max;
for (var i = 0; i < arr.length; i++) {
var val = sumArray(arr[i]);
if (max === void 0 || val > max) {
max = val;
}
}
return max;
}
//TEST
//parameters
var runs = 10;
var tests = 100000;
//output area
var out = document.body.appendChild(document.createElement("pre"));
//test functions
function simulate1() {
var t = tests;
var dt = Date.now();
while (t--) {
getMaxSum1(data);
}
out.textContent += 'getMaxSum1 took: ' + (Date.now() - dt) + "ms\n";
requestAnimationFrame(simulate2);
}
function simulate2() {
var t = tests;
var dt = Date.now();
while (t--) {
getMaxSum2(data);
}
out.textContent += 'getMaxSum2 took: ' + (Date.now() - dt) + "ms\n";
requestAnimationFrame(simulate3);
}
function simulate3() {
var t = tests;
var dt = Date.now();
while (t--) {
getMaxSum3(data);
}
out.textContent += 'getMaxSum3 took: ' + (Date.now() - dt) + "ms\n\n";
if (runs--) {
requestAnimationFrame(simulate1);
}
}
//start
simulate1();
pre {
max-height: 200px;
overflow-y: scroll;
background-color: #eee;
}
I included OliverRadini's answer to compare the relative speed boosts.