Assigning a value to a variable changes value of right hand side - javascript

I'm new to JavaScript and feel like I must be missing something fundamental here!
I'm creating a function which sorts a list of integers list and returns the minimum value listSort[0].
function sortNumber(a,b) {
return a - b;
}
var min = function(list){
console.log(list[0]);
var listSort = list.sort(sortNumber);
console.log(list[0]);
console.log(listSort[0]);
return list[0];
}
Can anyone explain why the value of list[0] changes after list.sort(sortNumber) is assigned to listSort ?
Thanks!

The sort() function directly changes the array to which is applied. For example:
myArray.sort();
directly changes the content of myArray.
If you do not want to change the content of the original array you need to use a cloning function:
function mySortingFunction(list){
var newList = myCloningFunction(list);
newList.sort();
return newList;
}
There are several ways to clone an array:
Javascript fastest way to duplicate an Array - slice vs for loop
If you use AngularJS you can simply write:
var newList = angular.copy(list);

To get the minimum value of an Array, better use Math.min
var list = [4,2,4,2,6,5,2,4,3,5,2];
var min = Math.min.apply(null, list);

If i'm right, it's because when you use your assignment, you are executing list.sort(sortNumber) and so list change as per the assignment function.
Sooo... read like this :
- list.sort(sortNumber) => List change
- list is assigned to listSort
Despite this fact, I think you're going to far to find the min value. :P

Related

Why does reduce return the number itself and, as in functional programming, replace if and break?

There is a simple function, its essence is to count from a number (n) to 0.
But when using reduce, the function just doesn't work, and no matter how I rewrite it, it returns either an empty array, or undefined, or the number itself 2.
First, I created an array that will take n, then I created a reduce method in which currentValue will take n and subtract 1 from it, after accumulator it takes the resulting number and using the push method, add it to the list array, but I don’t understand how I should add a condition that if accumulator is equal to 0, then the function must be stopped.
const countBits = (n) => {
let list = [n];
let resultReduce = n.reduce((accumulator, currentValue) => {
accumulator = currentValue - 1;
list.push(accumulator);
});
return resultReduce;
};
console.log(countBits([2]));
Why isn't this working the way I intended it to?
reduce will run on each of the items in the array, with the accumulator (first argument to the callback function) being the value that is returned from the callback function's previous iteration. So if you don't return anything, accumulator will be undefined for the next iteration.
If you want to count from n to 0, reduce is not the way to go (as well as the fact that in your current implementation, you don't even use list which would contain all of your stored numbers from n to 0). I would advise that instead, you simply loop from n to 0 and push those values into an array like so:
const countBits = (n) => {
let list = [];
for (let i = n; i > -1; i--) {
list.push(i);
}
return list;
};
console.log(countBits(2));
Also note I've changed your syntax slightly in the function call - you were passing an array with a single element seemingly unnecessarily, so I just passed the element itself to simplify the code.
The answer by Jack Bashford is correct, but for completeness I would like to point out that generating a range of numbers is a common need. Libraries like Underscore, Lodash and Ramda provide a ready-to-use function for this purpose. You don’t have to write your own implementation every time you need something common and mundane like that; save the time and enjoy the fact that you can spend your time on something more groundbreaking instead.
console.log(_.range(2, -1, -1));
<script src="https://underscorejs.org/underscore-umd-min.js"></script>
Also for the sake of completeness, let’s consider how you might implement a downwards range function using reduce, anyway. reduce expects an input array, though it can also accept an object if using Underscore or Lodash. To make meaningful use of the input collection, we could generate a consecutive number for every element of the collection. For an array, we could of course just do _.range(collection.length - 1, -1, -1) instead, but for an object, or something that you don’t know the length of in advance, such as a generator, using reduce for this purpose might make sense. The mapDownwardsRange function below will do this:
function unshiftNext(array) {
const front = array.length ? array[0] : -1;
return [front + 1].concat(array);
}
function mapDownwardsRange(collection) {
return _.reduce(collection, unshiftNext, []);
}
console.log(mapDownwardsRange(['a', 'b', 'c']));
<script src="https://underscorejs.org/underscore-umd-min.js"></script>

Looping through an array (`Object.keys(obj)`) to find a value

I'm learning Javascript, so pardon any mistakes in how I phrase the question.
I am writing a chess program to practice and learn. Currently, I am trying to write a function to find the color of a piece with the position as the parameter. The relevant pieces of code are as follows. The first two work as they were designed to, but the last does not.
let allPieces = board.getElementsByClassName('piece');
This sets allPieces as an object with the key values the html elemnts representing each piece, both black and white.
const getPiecePosition = function(element) {
let position = window.getComputedStyle(element).getPropertyValue('grid-row-start');
let letterIndex = alphabet.findIndex(function(letter) {
return letter === position[0];
});
letterIndex += 1;
return [letterIndex, Number(position[1])];
}
This takes a parameter in the form of the allPieces object with a specific key and returns the position as an array with the column number first and the row number second. ex. [2,3].
const getPieceByPosition = function(position) {
let pce = Object.keys(allPieces).forEach(function(piece) {
if (getPiecePosition(allPieces[piece]) == position) {
return allPieces[piece].borderColor;
}
})
return pce;
}
This is the function I am having trouble with. The idea behind it is that it will take each key in the allPieces object and loop through them using forEach() into the getPiecePosition() function to compare it with the position entered as the parameter. Since only one piece can inhabit any tile at once, it should never return multiple values.
I honestly don't know where to start debugging this code, but I have been trying for about an hour. It always just returns undefined instead of a truthy value of any kind.
Your last function has a few issues:
getPiecePosition(allPieces[piece]) == position
Assuming position is an array, you're trying to compare an array with an array here using ==. However, since the two arrays are different references in memory, this will always give false, even if they contain the same elements:
console.log([2, 3] == [2, 3]); // false
You're trying to return from the callback of .forEach(). This won't achieve what you want, as return will jump out of the .forEach callback function, not your outer getPieceByPosition() function. This leads me to your final issue:
The .forEach() method doesn't return anything. That is, it doesn't evaluate to a value once it is called. This means that let pce will always be undefined since you're trying to set it to the return value of .forEach(). This, in contrast to let letterIndex, is different, as letterIndex is set to the return value of .findIndex(), which does have a return value and is determined by the function you pass it.
One additional thing you can fix up is the use of Object.keys(allPieces). While this works, it's not the best approach for looping over your elements. Ideally, you would be able to do allPieces.forEach() to loop over all your elements. However, since allPieces is a HTMLCollection, you won't be able to do that. Instead, you can use a regular for loop or a for..of loop to loop over the values in your HTMLCollection.
Alternatively, there is a way to make allPieces.forEach() work.
Instead of using board.getElementsByClassName('piece');, you can use the method .querySelectorAll('.piece'), which will give you a NodeList. Unlike a HTMLCollection, a NodeList allows you to use .forEach() on it to loop through its elements.
The return type of getElementsByClassName HTMLCollection Object. You should't use Object.keys to loop through each of 'piece' element. Insted, use the follow.
for(var i = 0 ; i < allPieces.length ; i++){
var piece = allPieces[i];
... // and, do whatever with the getPiecePosition(piece)
}

Possible to .push() to a dynamically named key?

I have an object that I would like to push an indeterminate amount of other objects to, using a loop. To keep it organized, I'd like to dynamically name the keys based on them amount of times the loop runs. I have the following:
let formsJson = {};
let counter = 1;
//savedForms are where the objects that I want to push reside
savedForms.forEach( form => {
formsJson['form'+counter] = JSON.parse(form.firstDataBit_json);
//This is where I'm having trouble
counter = counter + 1;
});
I can push the first bit of data fine, and name the key dynamically as well. But I need to push 2 more objects to this same dynamic key, and that's where I'm having trouble. If I try the obvious and do:
formsJson['form'+counter].push(JSON.parse(form.secondDataBit_JSON));
I don't get any output. Is there a way to accomplish this?
forEach() gives you access to the index already. No need to create the counter variable. Example usage. I would definitely recommend using a simple index, and not using the 'form'+counter key.
In your example, it's not clear to me that the value being assigned in the forEach loop is an array. So it's unclear if you can push to any given element in that. But generally that syntax should
Personally, I would prefer to have a function that outputs the entire value of the element. That would provide better encapsulation, testability, and help enforce default values. Something like:
function createItem(param1) {
let item = [];
item.push(param1.someElement);
if (foo) {
item.push(...);
} else {
item.push(...);
}
return item;
}
formsJson['form'+counter] = createItem( JSON.parse(form) )
So you're making formsJson['form'+counter] a by assigning the JSON parse, not an array as you want. Try this:
formsJson['form'+counter] = [];
formsJson['form'+counter].push(JSON.parse(form.firstDataBit_json));
formsJson['form'+counter].push(JSON.parse(form.secondDataBit_JSON));
Maybe you want to figure out something like this
savedforms.forEach((form, index) =>
formsJson[`form${index + 1}`] = [ JSON.parse(form.secondDataBit_JSON)])
Now you can push on the item
formsJson[`form${index + 1}`].push(JSON.parse(form.secondDataBit_JSON));`
Also here you'll save operation on incrementing it will be automaticly

Prevent 2d array to mutate, javascript

I have an array which I want stay the same until I say otherwise. How do I prevent it from changing?
var orgArr = [[1,1], [2,2]];
function modArr(arr){
arr[0][0] = 5;
return arr;
}
modArr(orgArr);
console.log(orgArr); // It has changed
https://jsfiddle.net/50e6k7mc/
I tried some with immutable (which I added to the fiddle).
Something like
var orgArr = Immutable.List([[1,1],[2,2]])
Then using toArray(). But I cant get it to work.
In your modifying function you need to convert first from Immutable , modify and then convert back to Immutable
arr=arr.toArray();
//.... modify arr
//....
arr= arr.fromJS(arr);
here is the updated code :
https://jsfiddle.net/zvhp5k91/

Using a variable as an array parameter in javascript?

I want to use a variable as the index parameter in an array but for some reason it's coming up as "undefined" when I do it this way. Any ideas?
var object_number = [];
var x = 1;
function create_new_object()
{
object_number[x] = new happy_object();
x++;
}
Array indices start at zero in JavaScript. When x starts at 1, there's an undefined element in the first position of the array. Start at x=0 instead.
There's a better way to do this, however. Instead of manually keeping track of the last element in the list, you can just use Array.push() to add a new element onto the end.
var object_number = [];
function create_new_object()
{
object_number.push(new happy_object());
}
When you want to find out how many elements are in the array, use Array.length (or the number returned by Array.push()).
Further reference: Array # MDC.
your object_number is an empty array with no elements. Hence you are getting this error.
To add elements to array, you need to use push method.
object_number.push(new happy_object() );
Also you need to start your array index from 0 instead of 1. i.e. your x should be 0.
In addition to the previous answers:
Unlike "normal" languages (which use block-scope), javascript uses function-scope.
So be shure x exists in side your function-scope when you use it like that.
you can't mess with array keys too too much in js - if you want a more solid, definable relationship, you'll need to mimic an associative array by doing:
var object_number = {};
var x = 1;
function create_new_object() {
object_number[x] = new happy_object();
}
Also, I'd combine your var statements. Makes variable/function hoisting a bit clearer.
var object_number = {}
, x = 1;

Categories

Resources