Efficient way to create nested objects JavaScript - javascript

Are there any more efficient ways to create nested objects than nested for approach?
I am trying to hold 25 random numbers (1 - 71), 5 row x 5 column. I will iterate through each item to check if ballPicker() function (not yet implemented) has picked a number that the object has. So I need to keep the information that if a number was found. I could have done it with a 5 x 5 array but iterating that array will not be as efficient as objects.
I have an array starting from 1 to 71 (inclusive, step is 1).
I shuffle the array in createRandomNumberArray() function. So shuffledNumbers array values are not sequential
let shuffledNumbers = Array.from({length: 71}, (v, i) => i + 1);
function createRandomNumberArray() { //Fisher - Yates shuffle algorithm
let randomPosition;
let temp;
for (let i = shuffledNumbers.length - 1; i > 0; i--) {
randomPosition = Math.floor(Math.random() * (i + 1));
temp = shuffledNumbers[i];
shuffledNumbers[i] = shuffledNumbers[randomPosition];
shuffledNumbers[randomPosition] = temp;
}
}
In createBoards() function I am creating an object (board) that holds 5 objects (row) with their indexes as property name. And row has 5 objects (cell) with their indexes as property name again. Cell has only one object that gets its property name from slicedArray(slicedArray is a 5 item slice from shuffledNumbers that holds non duplicated random numbers (range inclusive 1 to inclusive 71)) and its value is always false.
function createBoards() {
let board = {};
for (let i = 0; i < 5; i++) {
let row = {};
let slicedArray = shuffledNumbers.slice((i * 5), ((i + 1) * 5));
for (let j = 0; j < 5; j++) {
let cell = {};
cell[slicedArray[j]] = false;
row[j] = cell;
}
board[i] = row;
}
return board;
}
I want to keep the structure like this:
Board object will have exactly 5 rows and 1 row has exactly 5 items. And in the future I need to iterate those items to find the random value that I had assigned before. I thought it is faster to use objects than arrays. I hope I am correct.

Related

Why does modifying an array of primitives reduce its performance?

I am looking into the performance of object lookup vs array lookup for a particle system.
Each particle just has x, y values. I can either store these in an object, array, or two separate arrays, one for x and one for y.
In order to iterate over all of my particles I have to store all of the objects in a big array anyway, accessing each object and its properties in order to move and draw the particle. I thought it would be faster to instead store the x, y values directly in arrays, but this jsben.ch test seems to show otherwise, but only when my arrays are sorted randomly. https://jsben.ch/ZR0Ff
The values in the arrays are going to change as particles are updated, added, and removed.
What I don't understand is if my arrays for x and y are just full of numbers and the array behind the scenes is a typed array, how does changing the bits at each index change the performance of the array?
Here is my code:
// The thinking behind this test is that it would be faster to iterate over
// arrays containing primitive values than objects
// Instead of storing data in objects, it would all be stored in arrays
// The values at the same index would belong to the same "object"
// In this way you might access the same data more efficiently
var objects = []; // object array full of objects with x and y properties
var x_values = []; // array of numbers representing x values
var y_values = []; // array of numbers representing y values
var combined_values = []; // array of x, y values in the same array
for (let i = 0; i < 10000; i ++) { // populate arrays
objects[i] = { x:i, y:i }; // each object has x and y
x_values[i] = i; // this is the array for x
y_values[i] = i; // this is the array for y
combined_values[i*2] = i; // this array goes x,y, x,y, x,y... and is twice as long
combined_values[i*2+1] = i;
}
// shuffling arrays gives interesting results
//shuffleArray(objects);
//shuffleArray(x_values);
//shuffleArray(y_values);
//shuffleArray(combined_values);
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
function testObjects(objects_) { // loop over all objects
var i = objects.length;
while (i > 0) { i -= 1;
objects_[i].x ++;
objects_[i].y ++;
}
}
function testCombinedValues(combined_values_) { // loop over combined values array
var i = combined_values_.length;
var j = i + 1;
while(i > 0) { i -= 2; j -= 2;
combined_values_[i] ++;
combined_values_[j] ++;
}
}
function testValues(x_values_, y_values_) { // loop over two separate values arrays
var i = x_values_.length;
while(i > 0) { i -= 1;
x_values_[i] ++;
y_values_[i] ++;
}
}
Go to the https://jsben.ch/ZR0Ff and try uncommenting the lines that shuffle the array values. I cannot understand why shuffling an array of numbers would have any bearing on its performance against an array of objects. It seems to me like it should always be significantly faster.

Finding target sum of three numbers - optimizing nested loops

I have a problem where, given an array of integers, I need to find sets of three numbers that add up to equal zero. The below solution works but isn't as optimal as I'd like and I am looking for ways to optimize it to avoid unnecessary processing.
What I am doing below is I am iterating through the all combinations of numbers while eliminating iterating through the same indices in each nested loop and I am checking if the three numbers in the inner most loop add up to zero. If yes, I am converting the array to a string and if the string isn't already in the results array I am adding it. Right before returning I am then converting the strings back to an array.
I appreciate any suggestions on how to further optimize this or if I missed out on some opportunity to implement better. I am not looking for a total refactor, just some adjustments that will improve performance.
var threeSum = function(nums) {
const sorted = nums.sort();
if(sorted.length && (sorted[0] > 0 || sorted[sorted.length-1] < 0)) {
return [];
}
let result = [];
for(let i=0; i < sorted.length; i++) {
for(let z=i+1; z < sorted.length; z++) {
for(let q=z+1; q < sorted.length; q++) {
if(sorted[i]+sorted[z]+sorted[q] === 0) {
const temp = [sorted[i], sorted[z], sorted[q]].join(',');
if(!result.includes(temp)) {
result.push(temp);
}
}
}
}
}
return result.map(str => str.split(','));
};
Sample Input: [-1,0,1,2,-1,-4]
Expected Output: [[-1,-1,2],[-1,0,1]]
One obvious optimisation is to precalculate the sum of the two first numbers just before the third nested loop. Then compare in the third loop if that number equals the opposite of the third iterated number.
Second optimisation is to take advantage of the fact that your items are sorted and use a binary search for the actual negative of the sum of the two first terms in the rest of the array instead of the third loop. This second optimisation brings complexity from O(N3) down to O(N2LogN)
Which leads to the third optimisation, for which you can store in a map the sum as key and as value, an array of the different pairs which sum to the sum so that each time you want to operate the binary search again, first you check if the sum already exists in that map and if it does you can simply output the combination of each pair found at that sum’s index in the map coupled with the negative sum.
The OP's solution runs in O(N³) time with no additional storage.
The classic "use a hash table" solution to find the missing element can bring that down to O(N²) time with O(N) storage.
The solution involves building a number map using an object. (You could use a Map object as well, but then you can't be as expressive with ++ and -- operators). Then just an ordinary loop and inner loop to evaluate all the pairs. For each pair, find if the negative sum of those pairs is in the map.
function threeSum(nums) {
var nummap = {}; // map a value to the number of ocurrances in nums
var solutions = new Set(); // map of solutions as strings
// map each value in nums into the number map
nums.forEach((val) => {
var k = nummap[val] ? nummap[val] : 0; // k is the number of times val appears in nummap
nummap[val] = k+1; // increment by 1 and update
});
// for each pair of numbers, see if we can find a solution the number map
for (let i = 0; i < nums.length; i++) {
var ival = nums[i];
nummap[ival]--;
for (let j = i+1; j < nums.length; j++) {
var jval = nums[j];
nummap[jval]--;
var target = -(ival + jval); // this could compute "-0", but it works itself out since 0==-0 and toString will strip the negative off
// if target is in the number map, we have a solution
if (nummap[target]) {
// sort this three sum solution and insert into map of available solutions
// we do this to filter out duplicate solutions
var tmp = [];
tmp[0] = ival;
tmp[1] = jval;
tmp[2] = target;
tmp.sort();
solutions.add(tmp.toString());
}
nummap[jval]++; // restore original instance count in nummap
}
nummap[ival]--;
}
for (s of solutions.keys()) {
console.log(s);
}
}
threeSum([9,8,7,-15, -9,0]);
var threeSum = function(unsortedNums) {
const nums = unsortedNums.sort();
if(nums.length && (nums[0] > 0 || nums[nums.length-1] < 0)) {
return [];
}
const result = new Map();
for(let i=0; i < nums.length; i++) {
for(let z=i+1; z < nums.length; z++) {
for(let q=z+1; q < nums.length; q++) {
if(nums[i]+nums[z]+nums[q] === 0) {
const toAdd = [nums[i], nums[z], nums[q]];
const toAddStr = toAdd.join(',');
if(!result.has(toAddStr)) {
result.set(toAddStr, toAdd);
}
}
}
}
}
return Array.from(result.values());
};

Why should length of the array be stored in selection sort algorithm?

The algorithm code from Grokking algorithm book:
const findSmallestIndex = (array) => {
let smallestElement = array[0]; // Stores the smallest value
let smallestIndex = 0; // Stores the index of the smallest value
for (let i = 1; i < array.length; i++) {
if (array[i] < smallestElement) {
smallestElement = array[i];
smallestIndex = i;
}
}
return smallestIndex;
};
// 2. Sorts the array
const selectionSort = (array) => {
const sortedArray = [];
const length = array.length;
for (let i = 0; i < length; i++) {
// Finds the smallest element in the given array
const smallestIndex = findSmallestIndex(array);
// Adds the smallest element to new array
sortedArray.push(array.splice(smallestIndex, 1)[0]);
}
return sortedArray;
};
console.log(selectionSort([5, 3, 6, 2, 10])); // [2, 3, 5, 6, 10]
The problem is in the function selectionSort, storing the array length in the variable wes necessary to make it work correctly and this one i couldn't understand, i tried to not store the length in a variable:
const selectionSort = (array) => {
const sortedArray = [];
for (let i = 0; i < array.length; i++) {
// Finds the smallest element in the given array
const smallestIndex = findSmallestIndex(array);
// Adds the smallest element to new array
sortedArray.push(array.splice(smallestIndex, 1)[0]);
}
return sortedArray;
};
console.log(selectionSort([5, 3, 6, 2, 10])); // [2, 3, 5]
I guessed that the problem may be the splice method because it reduces the length every time in the loop but i think the index is not important here, so it may not be be the problem!
Your code is removing the element from the original array, so on each iteration, i++ increases i and also splice decreases array.length. That means i and array.length get closer together by 2 each time instead of by 1, so the loop only iterates half as many times as you want it to. That means you only sort half of the elements into sortedArray.
By copying const length = array.length; first, the variable length is not changed inside the loop, so the i++ makes i closer to length on each iteration by 1, so the number of iterations is the original array length, and every element gets sorted.
As a side note, your algorithm sorts into a new array, but leaves the original array empty. That's probably never what you want; a sorting algorithm should either sort the array in-place (leaving the original array in sorted order), or return a new sorted array (leaving the original array unchanged). You could fix this by making a copy of array at the start of your function, so the algorithm destroys the copy instead of the original.
I'm putting this here for the sole reason that the posted implementation, apparently from an algorithms text, is needlessly overcomplicated and inefficient as well.
function selectionSort(array) {
function smallestIndex(start) {
let si = start;
for (let i = start + 1; i < array.length; ++i) {
if (array[i] < array[si])
si = i;
}
return si;
}
for (let i = 0; i < array.length; i++) {
let index = smallestIndex(i), t;
// swap value into current slot
t = array[index];
array[index] = array[i];
array[i] = t;
}
return array;
}
Here, the smallestIndex() function is enhanced to take a starting position as a parameter. Thus it finds the index of the smallest value in the remainder of the array. On the first iteration, that'll be the smallest value in the whole array. That value is swapped with whatever is at the current starting point, so after that first time through the main loop position 0 in the array is the smallest value in the whole array.
On the next iteration, the search for the index starts at 1, so that process will find the second smallest value from the original array, and swap that into position 1.
The process continues through the array. Note that no new arrays are constructed, and there are no calls to linear-time Array methods.

javascript multidimensional Typed array (Int8Array) example

I tried to use Typed arrays instead of arrays, to reduce memory:
function createarrayInt8(numrows,numcols,number){
var arr = new Int8Array(numrows);
for (var i = 0; i < numrows; ++i){
var columns = new Int8Array(numcols);
for (var j = 0; j < numcols; ++j){
columns[j] = number;
}
arr[i] = columns;
}
return arr;
}
But i can't create multidimensional Typed array.
Why?
Do i have to cast only the "number" var to Int8?
A typed Int8Array can only hold 8-bit integers. So arr[i] = columns won't work since columns is of type Int8Array which cannot be converted to and stored (in any meaningful way) as a an 8-bit integer.
Solution: Either make arr a generic Array whose elements can be arrays or - probably the more advanced but usually more performant solution - store your multidimensional array as a single flat array of size numrows * numcols and access an element via arr[column + row * numcols]:
var numrows = 5, numcols = 4;
var arr = new Int8Array(numrows * numcols).fill(0);
arr[3 + 1 * numrows] = 1; // col = 3, row = 1
console.log (arr);

Selecting object in 2 dimensional arrays js

I've got a 2 dimensional array (I'll call it myArray). myArray is full of 10 child arrays, each with 10 "-"s.
function mapInit () {
max_size = 10;
board = [];
map_width = Math.floor((Math.random() * max_size + max_size) / 2);
map_height = Math.floor((Math.random() * max_size + max_size) / 2);
boardsize = { x : map_width, y : map_height }
}
function generateBoard () {
var columns = [];
for (var i = 0; i < map_height; i++) {
columns.push("---");
};
for (var i = 0; i < map_width; i++) {
board.push(columns);
};
}
When I select myArray[x][y], it returns the value of the single object in that array: "-". This makes sense because I asked for that individual value.
When I set myArray[x][y] = 1, it sets all [y] in the second-level arrays to 1. It should set the individual value in that specific child array to 1, because the individual value is what was just returned when I selected myArray[x][y]. What am I doing wrong / not understanding?
What am I doing wrong / not understanding?
You adding a reference to a single array multiple times to another array. Take a look at this simplified example:
var a = [];
var b = [a, a];
a[0] = 42;
console.log(b);
// [[42], [42]]
as you can see, I'm setting a as the first and second element in the b array. There is no reason why this operation should create two copies of a in the process. Both elements reference the same array, which you can easily test with
b[0] === b[1] // true
Two distinct arrays would never be equal to each other ([] === [] returns false).
Solution: Create a copy of the array inside the second loop:
for (var i = 0; i < map_width; i++) {
board.push(columns.slice(0));
}

Categories

Resources