Can I create a variable that references a node in an object? - javascript

I have this piece of code. It increments a value in an array or sets it to 1 if undefined:
if(typeof sum[period][count] === "undefined"){
sum[period][count] = 1
}else{
sum[period][count]++;
}
I would like to make it shorter and less repetitive like this:
node = sum[period][count];
if(typeof node === "undefined"){
node = 1
}else{
node++;
}
or even
node = typeof node === "undefined"? 1 : node+1;
But it doesn't work, why not and what can I do about it?

since the value sum[period][count] is a primitive, you can't get a reference to it. You could shorten it by assigning the object containing this value to a variable: var node = sum[period] and test for node[count]. However, it is not much shorter:
var node = sum[period];
if(typeof node[count] === "undefined"){
node[count] = 1
}else{
node[count]++;
}

Because the node will have the value not the Reference.

I believe your solution will work in some cases, but in others it will not. This is a consequence of using an array of arrays (two-dimensional array/hash) and only checking if the 2nd order value is undefined.
When you define a list of lists (e.g. 'sum') you don't create a 2D matrix. Instead, you are creating a 1D list of 1D lists. If the first list index (period) is found, then it will get that list and look for the second index (count) within that 2nd list. But if the first list doesn't contain that first index, then you'll get an error immediately since you are referencing an index in an undefined list.
The solution is to check if the first order index value is undefined before checking the second. For reusability, I'm packaging the code up in a function. You will want to remove the JQuery output; it's just added for my testing.
var sum = {0: {0: 2}};
var period = 0;
var count = 1;
var helper = function(idxa, idxb) {
if (sum[idxa] === undefined) {
sum[idxa] = {idxb: 1}; // we can define the new list, knowing idxb must be 1 now.
} else {
// you were onto the right code here, but you weren't validating sum[idxa] separately.
sum[idxa][idxb] = (sum[idxa][idxb] === undefined) ? 1 : sum[idxa][idxb] + 1;
}
$('#output').append(" After: " + sum[idxa][idxb]);
};
helper(0, 3); // outputs " After: 1". [0] was defined, but [0][3] wasn't.
helper(2, 3); // outputs " After: 1". Neither [2] nor [2][3] were defined.
helper(0, 0); // outputs " After: 3", because [0][0] was 2
helper(0, 0); // outputs " After: 4", because we already incremented to 3.
You could nest a ternary operation inside a ternary, but I didn't because it's no faster and it wouldn't improve readability.

Related

using Short-Circuiting ( || ) in Object in JavaScript

this block of code takes the values of the scored property and puts them in duplication object as a key and how many times the name got duplicated as a value using the || operator.
I understand that the || operator will return the first truthy value or the last value if all of the values are falsy, however, I didn't understand duplication[x]++ what does the ++ sign do exactly? and why we put the (duplication[x]=1) between parentheses
const game = {
score: "4:0",
scored: ["Lewandowski", "Gnarby", "Lewandowski", "Hummels"],
};
const duplication = {};
for (let x of game.scored) {
duplication[x]++ || (duplication[x]=1) // I'm confused in this line
}
console.log(duplication);
Let's see what's happing on this line :
duplication[x]++ || (duplication[x]=1)
duplication[x]++ , first duplication[x] it will check if duplication has any with value of x, if yes then it it will perform duplication[x]++ else it will be undefined to moved to the other part of or condition
duplication[x]=1, this is a simple assignment it will assign the value 1, duplication[x] and this will create a key if not exist in the duplication object
Now if you run the below script and check the console log for each loop, it will give you clear idea what actually happing.
const game = {
score: "4:0",
scored: ["Lewandowski", "Gnarby", "Lewandowski", "Hummels"],
};
const duplication = {};
let index = 0;
for (let x of game.scored) {
console.log( `INDEX : ${index} ` , x , duplication[x] ? 'FOUND , INCREMENT CURRENT VALUE WITH ++' : 'NOT FOUND, SO ASSIGN VALUE 1' );
duplication[x]++ || (duplication[x]=1)
console.log( `INDEX : ${index} \n` , duplication);
index++;
}
console.log( 'FINAL OBJECT \n' , duplication);
The non-descriptive variable names don't really help to explain the situation. Let's start of by rewriting the code with more descriptive variable names.
const game = {
score: "4:0",
scored: ["Lewandowski", "Gnarby", "Lewandowski", "Hummels"],
};
const goals = {};
for (const player of game.scored) {
goals[player]++ || (goals[player] = 1);
}
console.log(goals);
goals[player]++ increments the goals for player by 1 and returns the old value. The tricky thing in goals[player]++ is that player might not be present in goals yet. In which case undefined is returned (which is falsy). Because the value is falsy the second operand of the OR operator will be executed. (goals[player] = 1) will set the goals for player to 1.
The code is essentially counting how often a specific name is present in the game.scored array. The presence of a name symbolises a goal made by them.
A less cryptic way of writing similar code would be:
const goals = {};
for (const player of game.scored) {
if (player in goals) {
goals[player] += 1; // add 1 to the current score
} else {
goals[player] = 1; // no score present so use 1
}
}
However I usually prefer to set a default value, this way you don't have to split the logic into two actions:
const goals = {};
for (const player of game.scored) {
goals[player] ||= 0; // assign 0 if the current value is falsy
goals[player] += 1; // add 1 to the current value
}
Note that ||= is fairly new, if you write JavaScript for older browser you can use the following instead:
if (!goals[player]) goals[player] = 0;
The first part of
duplication[x]++ || (duplication[x] = 1)
^^^^^^^^^^^^^^^^
has four parts:
a variable duplication with
a property accessor x in bracket notation
a postfix increment operator ++ and
an expression for the logical OR || operator.
The second part returns undefined at the first call with an unknown property.
The try to increment this value returns NaN, because of the following operation of duplication[x] = duplication[x] + 1. The result is is a falsy value.
This forces the expression to evaluate the right hand part of logical OR.
And because the left hand part has an expression, it needs to be evaluated first with a grouping operator (). Now the assignment takes place and the result of 1 is returned to the OR.

How to get the underscore function _.first to work on the arguments objects?

As part of the precourse for a coding bootcamp, we have to create a simpler version of the underscore JS library. I am struggling with creating the _.first function, which:
Returns an array with the first n elements of an array.
If n is not provided it returns an array with just the first element.
This is what I've got so far:
_.first = function(array, n) {
if (!Array.isArray(array)) return [];
if (typeof n != "number" || n <= 0) return [].slice.call(array, 0, 1);
return n >= array.length ? array : [].slice.call(array, 0, n);
};
It passes all test except one: "It must work on an arguments object"
I know the arguments object passes an array with all the arguments passed and it has a length property but Im struggling to work with it.
Any help would be much appreciated.
The arguments object is just that, a variable defined implicitly on each function scope that acts like an array. Has a length property and you can access the elements by using number properties like a normal array:
var _ = {};
_.first = function() {
if (arguments.length == 0) { // If there's no arguments
return [];
} else { // When there's 1 or more arguments
var array = arguments[0];
var n = arguments.length > 1 ? arguments[1] : 1; // If there's only the "array" argument ("n" is not provided), set "n" to 1
// And now your code, which has nice checks just in case the values are invalid
if (!Array.isArray(array)) {
return [];
}
if (typeof n != "number" || n <= 0) {
n = 1;
}
return [].slice.call(array, 0, n); // Don't worry if slice is bigger than the array length. It will just work, and also always return a copy of the array instead of the array itself.
}
};
console.log( _.first() );
console.log( _.first([0,1,2]) );
console.log( _.first([0,1,2], 2) );
console.log( _.first([0,1,2], 10) );
I would add something to the first answer. The arguments object is something that is normally created implicitly by JavaScript and made available inside the function body. In order to write a unit test for "It must work on an arguments object", they must explicitly define an arguments object and pass it in. This is a bad unit test because it is testing the internal working of your function. You should be free to write the function any way you like, and a unit test should test the external behaviour of the function (return value and/or side effects, based on the arguments passed).
So imo your original solution is good and the test is designed to force you to use a certain syntax for the sake of learning, but this is misleading.

chineseFood[array[0]] = array[array.length-1];

I don't understand the purpose of this = sign on the sixth line in the code block below. I understand how the argument grabs each index number of the array, I just don't understand why chineseFood[array[0]] = array[array.length-1]; In other words, I don't get the purpose of the equal sign as if it were almost comparing each other to be stored in the empty object that is stored in the variable chineseFood. Could someone please clarify? It would be much appreciated.
function transformFirstAndLast(array) {
var chineseFood = {};
//takes 1st element (at index 0) and sets it to the last element (nth index): array(length-1)
chineseFood[array[0]] = array[array.length - 1];
return chineseFood;
}
console.log( transformFirstAndLast(['Orange', 'Lemon', 'Pork', 'Chicken']) );
Output Below
{Orange: "Chicken"}
The equals sign is not comparison, it is assignment. chineseFood is an object, which means that it can be treated like a dictionary, and its properties can be accessed using the [] operator instead of the . operator:
myObj = {
foo: "bar"
};
console.log(myObj["foo"]); // bar
console.log(myObj.foo); // bar
Likewise, you can also assign properties this way:
myObj = {};
myObj["foo"] = 3;
console.log(myObj["foo"]); // 3
console.log(myObj.foo); // 3
This is what your code is doing. It is retrieving the value of array[array.length-1], which is "Chicken". Then it is assigning this value to the property of chineseFood that has the name represented by array[0], which happens to be "Orange". Thus, the property named Orange on chineseFood is set to array[array.length - 1], which is why chineseFood evaluates to {Orange: "Chicken"}.
This method of accessing properties is especially useful when you don't know the name of the property you will be changing in advance, as is the case with this code, or when you want to create properties that have names that would otherwise be illegal:
myObj = {
".you can't usually use with spaces or start w/ periods": false
};
myObj[".you can't usually use with spaces or start w/ periods"] = true;
console.log(myObj[".you can't usually use with spaces or start w/ periods"]);
// there is no way to read this property the normal way
Basically what is does is:
your object is :
var obj = {Orange: "Chicken"};
And Your array is :
var arr = ['Orange','Lemon','Pork','Chicken']
What this line says is pick first element of the array and check for this prop in object and change its value to last element of array, here:
arr[0] = "orange";
So this line :
obj[arr[0]] can be seen as obj['orange'].
After that you change its value:
Obj[arr[0]] = arr[arr.length-1] which can be written as obj['orange'] = 'chicken'

jQuery - Iterate over Objects with numeric keys

I have an Object:
var myObj = { 5: "foo", 25: "bar" };
Now i want to walk through the object like this:
$.each(myObj, function(key, value) {
console.log(value);
});
The problem is, that jQuery walks 26 times through the array while value will be "undefined" in 24 cases (i debugged it - thats fact). So it walks through the object 5 times before it reaches the fifth value (which is the first one).
I dont want to convert the object to an array since i want to store the data back into the stack after handling the data.
Is this a jQuery-Bug, a bug of mine or how to solve this?
Additional Info:
Here's a screenshot of my Chrome's DevTools shows the object at runtime.
sT.data.tasks contains the same content as splitTicket - just needed to show splitTicket because sT.data.tasks is lost inside the $.each-Scope. (Maybe that indicates the problem?) - Another thing to note is the "length" of the object with key "2517" - Is that a Google-Chrome Bug, a javascript Bug or just correct?
-> Note that at the current breakpoint tkey is 0 (int) and tval is undefined (undefined) and since this code wont produce any Errors, it walks through it 2517 times (did not count^^)
It's not a bug at all. It looks like jQuery is just treating this object as an array and iterating over it in the normal way, starting from 0 and going to the largest key.
A for..in loop loops over the properties in the object.
Using the following only goes through 2 times:
for(key in myObj) {
console.log(myObj[key]);
};
Your example looks just fine to me:
var myObj = { 5: "foo", 25: "bar" };
$.each(myObj, function(key, value) {
console.log(value);
});
foo
bar
Looking at the jQuery source for $.each it appears to use for..in if your object isn't "arraylike".
I'd hypothesise that isArraylike returns true for your 'real' code (it's false for myObj)
jQuery $.each:
for ( i in obj ) {
value = callback.call( obj[ i ], i, obj[ i ] );
if ( value === false ) { break; }
}
jQuery isArraylike:
function isArraylike(obj) {
var length = obj.length,
type = jQuery.type(obj);
if (jQuery.isWindow(obj)) {
return false;
}
if (obj.nodeType === 1 && length) {
return true;
}
return type === "array" || type !== "function" && (length === 0 || typeof length === "number" && length > 0 && (length - 1) in obj);
}
try this,
var myObj = { 5: "foo", 25: "bar" };
var i=0;
Object.keys( myObj ).forEach(function ( name, index ) {
var value = myObj[name];
console.log(++i);
console.log(name); // the property name
console.log(value); // the value of that property
console.log(index); // the counter
});
live DEMO
your code iterate two time at my side.but also you can try using this example.

Using array.splice inside Array prototype

Array.prototype.move = function(oldIndex, newIndex) {
var val = this.splice(oldIndex, 1);
this.splice(newIndex, 0, val[0]);
}
//Testing - Change array position
var testarray = [1, 2, 3, 4];
testarray.move(3, 0);
console.log(testarray);
This produces an error "this.splice is not a function" yet it returns the desired results. Why?
Array.prototype.move = function(oldIndex, newIndex) {
if(Object.prototype.toString.call(this) === '[object Array]') {
if(oldIndex && typeof oldIndex == 'number' && newIndex && typeof newIndex == 'number') {
if(newIndex > this.length) newIndex = this.length;
this.splice(newIndex, 0, this.splice(oldIndex, 1)[0]);
}
}
};
For some reason, the function is being called by the called by the document on load (still haven't quite figured that one out). I added a few checks to verify that this = an array, and then also reset the new index to be equal to the total size if the supplied int was greater than the total length. This solved the error issue I was having, and to me is the simplest way to move objects around in an array. As for why the function is being called onload must be something to do with my code.
You don't need the placeholder variable-
Array.prototype.move = function(oldIndex, newIndex) {
this.splice(newIndex, 0, this.splice(oldIndex, 1)[0]);
}
var a=[1,2,3,4,9,5,6,7,8];
a.move(4,8);
a[8]
/* returned value: (Number)
9
*/
Adding properties to built–in objects is not a good idea if your code must work in arbitrary environments. If you do extend such objects, you shouldn't use property names that are likely to be used by someone else doing the same or similar thing.
There seems to be more than one way to "move" a member, what you seem to be doing can be better named as "swap", so:
if (!Array.prototype.swap) {
Array.prototype.swap = function(a, b) {
var t = this[a];
this[a] = this[b];
this[b] = t;
}
}
I expect that simple re-assignment of values is more efficient than calling methods that need to create new arrays and modify the old one a number of times. But that might be moot anyway. The above is certainly simpler to read and is fewer characters to type.
Note also that the above is stable, array.swap(4,8) gives the same result as array.swap(8,4).
If you want to make a robust function, you first need to work out what to do in cases where either index is greater than array.length, or if one doesn't exist, and so on. e.g.
var a = [,,2]; // a has length 3
a.swap(0,2);
In the above, there are no members at 0 or 1, only at 2. So should the result be:
a = [2]; // a has length 1
or should it be (which will be the result of the above):
a = [2,,undefined]; // a has length 3
or
a = [2,,,]; // a has length 3 (IE may think it's 4, but that's wrong)
Edit
Note that in the OP, the result of:
var b = [,,2];
b.move(0,2);
is
alert(b); // [,2,];
which may not be what is expected, and
b.move(2,0);
alert(b); // [2,,];
so it is not stable either.

Categories

Resources