What does this Javascript code mean? - javascript

I am trying to do a algorithm challenge. I saw this code from one of the completed answers. I am new to javascript so I am not completely sure of the code. I know that the first section of code pretty much takes the input and makes it to a map. Then init a matchingPair array and a counter. I am pretty much lost at the for loop section. Not too sure what matchingPairs[""+c[i]] = matchingPairs[""+c[i]] || 0; does. I am guessing the for loop is adding all the elements of matching pairs to the array and adding to counter.
function main() {
var n = parseInt(readLine());
c = readLine().split(' ');
c = c.map(Number);
var matchingPairs = {};
var matchingPairCount = 0;
for(var i=0; i< c.length; i++) {
matchingPairs[""+c[i]] = matchingPairs[""+c[i]] || 0;
matchingPairs[""+c[i]] += 1;
if (matchingPairs[""+c[i]] % 2 === 0) {
matchingPairCount += 1;
}
}
console.log(matchingPairCount);
}

A simpler example
a = {}
a["b"] = a["b"] || 0
console.log(a) // {b: 0}
a is set to an empty object. a["b"] is not yet set to anything, so when accessed it returns undefined.
Undefined is falsey - so this effectively sets a["b"] to 0.
Take another example;
a = {b: 5}
a["b"] = a["b"] || 0
console.log(a) // {b: 5}
The only difference in this example is that a["b"] is truthy (ie, it returns 5 rather than undefined).
This is a javascript way of setting a variable to a value only if it hasn't already been set to something else.

A string of space delimited numbers is supplied.
The counts of each number are calculated.
Each time a count is even then the matchingPairCount is incremented.
In short: it is counting pairs of matching numbers.
n is not used?

Related

override array length in javascript

I got an idea to override javascript array length value and wants to return any random value.
let's say..
a=[56,78,89,200,800]
b=['a','b','f']
a.length = 2 ; //should give me only 2;
b.length = 2 ; //also should give 2
Is it possible to change length property and is there any tweaks to change the splice or slice method also.
Array.length is a protected property, only read; is a bad idea try to change it.
Better you can create your own class; example:
var oArray = function (aArr){
oRet = {};
for(var i = 0, aArrL = aArr.length; i < aArrL; i++){
oRet[i] = aArr[i];
}
oRet.length = 2;
return oRet;
};
a= new oArray([56,78,89,200,800]);
b=['a','b','f'];
console.log(a.length);
console.log(b.length);
a is a custom class, b is standard JavaScript Array.

Why array.indexOf(undefined) doesn't work if array is sparse

I'm new at JavaScript and there is one thing that bothers me. I have got a very simple code:
var a = [];
a[1] = 1;
i = typeof(a[0]);
index = a.indexOf(undefined);
len = a.length;
console.log(a);
console.log("\n" + len);
console.log("\n" + i);
console.log("\n" + index);
My question is: Why indexOf returns -1, instead of 0. I know that this method compare by ===, but I used as a parameter keyword undefined. If I change method parameter to "undefined" it also doesn't work (but this for me it's obvious). Can someone explain me this and tell what is the simpliest way to find undefined value in array?
This will in fact find an undefined value in an array, the problem is that your array a doesn't have any undefined values in it, so it returns -1 meaning it did not find any. Your array looks like:
[*uninitialized*, 1]
The fact that you put nothing in the first position doesn't mean it's populated with an undefined, it simply is not initialized/does not exist.
If you did something like:
var a = [undefined, 1];
var index = a.indexOf(undefined);
console.log(index);
It will in fact print 0 as expected.
Edit: to answer your question of how to find an uninitialized value, do the following
var a = [];
a[1] = 1;
for(var i = 0; i < a.length; i++){
if(a[i] === undefined){
console.log(i);
}
}
This will print the index of uninitialized array values. The reason this actually works unlike indexOf is that a[i] will evaluate to undefined if:
(1) The element exists and it has the value undefined, or
(2) The element doesn't exist at all. indexOf however will skip these "gaps" in the array.
In general, arrays in JavaScript are sparse – they can have holes in them (That's why indexOf() returns -1 ), because an array is simply a map from indices to values. The array you are looking for is called dense it looks like
var a = Array.apply(null, Array(3))
or
var a = Array(undefined, undefined, undefined)
a.indexOf(undefined) //0
Please have a look at this post, i hope it will help you

How does this for/in loop work exactly?

I'm doing a beginner exercise, find the mean/median/mode/range of an array of numbers. I'm on the mode now, and found this:
var store = ['1','2','2','3','4'];
var frequency = {}; // array of frequency.
var max = 0; // holds the max frequency.
var result; // holds the max frequency element.
for(var v in store) {
frequency[store[v]]=(frequency[store[v]] || 0)+1; // increment frequency.
if(frequency[store[v]] > max) { // is this frequency > max so far ?
max = frequency[store[v]]; // update max.
result = store[v]; // update result.
}
}
It works but I don't understand it.
What does the || 0 do in the first line?
Why can't I change the key names?
frequency["key"+store[v]]=(frequency[store[v]] || 0)+1; returns {key1: 1, key2: 1, key3: 1, key4: 1} not {1: 1, 2: 2, 3: 1, 4: 1}, so the keys are playing an important role.
Is the if statement testing both the key and value?
Replacing any instance of frequency[store[v]]; with a variable (var freqTest = frequency[store[v]];, created inside or outside the loop) breaks something.
The whole thing is going over my head really.
The key in the entire logic is understanding this line
frequency[store[v]]=(frequency[store[v]] || 0)+1;
The left side is being used as a map for some number. When v is equal to 3 store[3] returns 2 and thus frequency[2] is accessed.
Now for the same iteration consider the right side. We already know that
frequency[store[3]]
resolves to
frequency[2]
but what will this return? As frequency[2] would have also been set in iteration 2 we would be accessing the number from iteration 2. Lets now look at the value derived from iteration 2 then:
frequency[store[2]] = (frequency[store[2]] || 0)+1
frequency[2] = (frequency[2] || 0)+1
frequency[2] = (null || 0)+1
frequency[2] = 1
Ahhh... so the value for iteration 3 is actually
frequency[2] = (frequency[2] || 0) + 1
frequency[2] = (1 || 0) + 1
frequency[2] = (1) + 1
frequency[2] = 2
As you can see, the loop is using frequency[n] as a map and increments the value each time it is found. Then the value is stored in max if it is higher. This is a very smart way to find the highest repeating value while only iterating over the list one time.
What does the || 0 do in the first line?
It takes 0 as a default value when the lookup fails (when there is not yet a frequency property with that name), so that the map is initialised with 1s on the first appearance of a value, not NaN (from undefined + 1).
The assignment can (and for beginners, should) be expanded to
if (frequency[key]) // the property already exists, does not give `0` or `undefined`
frequency[key] = frequency[key] + 1;
else // the property didn't exist and the access yielded `undefined`
frequency[key] = 1; // 0 + 1
Why can't I change the key names?
You can, you just have to do it everywhere.
The code should be written much cleaner like this:
var store = ['1','2','2','3','4'];
var frequency = {}; // object (key-value-map) of frequency
var max = 0; // holds the max frequency value
var result; // holds the max frequency element name
for (var v=0; v<store.length; v++) {
var key = "key" + store[v];
frequency[key] = (frequency[key] || 0)+1; // increment frequency
// ^^^ here as well
if (frequency[key] > max) { // is this frequency > max so far ?
max = frequency[key]; // update max.
result = store[v]; // update result.
// ^^^^^^^^ alternatively use `key` also here
}
}
Is the if statement testing both the key and value?
Testing? Hm, no. It does use the value from the store array as a key in the frequency object. It does then compare the property value with the max.
a || 0 means if a is not undefined, take 1 otherwise 0
You can change the key names.
var store = ['1','2','2','3', '1', '1','4'];
var frequency = {}; // array of frequency.
var max = 0; // holds the max frequency.
var result; // holds the max frequency element.
for(var v in store) {
frequency['key'+store[v]]=(frequency['key'+store[v]] || 0)+1; // increment frequency.
if(frequency['key' + store[v]] > max) { // is this frequency > max so far ?
max = frequency[store[v]]; // update max.
result = 'key' + store[v]; // update result.
}
}
The line you ask about frequency[store[v]]=(frequency[store[v]] || 0)+1 is sometimes referred to OR assignment; this stack overflow question has some good explanations and examples. For your code, consider this that I just typed into my browser's javascript console:
> var frequency = {};
<- undefined
> frequency[0];
<- undefined
> frequency[0] || 0
<- 0
As for you why you can't change the key names, you can, you just haven't changed them 'enough'. Changing the body to replace every key reference with "key"+store leaves the code in the same functioning state.
for(var v in store) {
// Increment the frequency of the value v
frequency["key"+store[v]]=(frequency["key"+store[v]] || 0)+1;
// is this frequency > max so far ?
if(frequency["key"+store[v]] > max) {
// If it is, we have a new, most frequently occurring number
// Update the max to this new highest frequency
max = frequency["key"+store[v]];
// Update the result to the new most frequent value
result = store[v];
}
}
I added some additional comments in the code to make it clearer what's going on.
To your first question:
In JavaScript you can test if variables are defined by using them as booleans.
var foo;
if(foo) //true
console.log('foo is false, 0, null or not defined');
So in this case you are testing if frequency already has an element for store[v]. If it does, use that, otherwise use 0 instead so that would be the same as
var valueInFrequency = frequency[store[v]] ? frequency[store[v]] : 0;
and then continue with valueInFrequency.
To your second question: As I explained just now, in
frequency[store[v]]=(frequency[store[v]] || 0)+1;
you either raise the current value by one or set it to 0 and then raise it by one. If you change the key you set the value to but then don't test for the new value, you end up simply overriding the existing value to 0 + 1.
Now to your last question: No it isn't. It uses store[v] as a key for frequency and then compares that value to max.
I hope I could answer your questions. If anything is still unclear, just ask!
I propose a better solution to this problem as the given solution.
Solution with emphasis to Array.prototype.forEach and the problem of getting more than one key if the max count is shared among more items.
What has changed:
result is now an array, because the maximal count of the distribution can affect more than one key/item.
The for () loop is replaced by Array.prototype.forEach and a callback which allows an iteration over all ements of an array in a more compact manner.
Only max is in the callback stored.
For the keys/item with the max count is another loop necessary.
First get the keys from the object with Object.keys
Then iterates over the keys and check for count === max and push the key
Display all found values.
To the question what x = x || y means:
If the value of x is falsy (like undefined, null, 0, -0, '') the the value of y is used, because of the Logical Or operator.
var store = ['1', '2', '2', '3', '4', '5', '5'],
distribution = {},
max = 0,
result = [];
store.forEach(function (a) {
distribution[a] = (distribution[a] || 0) + 1;
if (distribution[a] > max) {
max = distribution[a];
}
});
Object.keys(distribution).forEach(function (k) {
distribution[k] === max && result.push(k);
});
document.write('max: ' + max + '<br>');
document.write('key/s with max count: ' + JSON.stringify(result) + '<br>');
document.write('<pre>' + JSON.stringify(distribution, 0, 4) + '</pre>');

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.

Count of "Defined" Array Elements

Given following array:
var arr = [undefined, undefined, 2, 5, undefined, undefined];
I'd like to get the count of elements which are defined (i.e.: those which are not undefined). Other than looping through the array, is there a good way to do this?
In recent browser, you can use filter
var size = arr.filter(function(value) { return value !== undefined }).length;
console.log(size);
Another method, if the browser supports indexOf for arrays:
var size = arr.slice(0).sort().indexOf(undefined);
If for absurd you have one-digit-only elements in the array, you could use that dirty trick:
console.log(arr.join("").length);
There are several methods you can use, but at the end we have to see if it's really worthy doing these instead of a loop.
An array length is not the number of elements in a array, it is the highest index + 1. length property will report correct element count only if there are valid elements in consecutive indices.
var a = [];
a[23] = 'foo';
a.length; // 24
Saying that, there is no way to exclude undefined elements from count without using any form of a loop.
No, the only way to know how many elements are not undefined is to loop through and count them. That doesn't mean you have to write the loop, though, just that something, somewhere has to do it. (See #3 below for why I added that caveat.)
How you loop through and count them is up to you. There are lots of ways:
A standard for loop from 0 to arr.length - 1 (inclusive).
A for..in loop provided you take correct safeguards.
Any of several of the new array features from ECMAScript5 (provided you're using a JavaScript engine that supports them, or you've included an ES5 shim, as they're all shim-able), like some, filter, or reduce, passing in an appropriate function. This is handy not only because you don't have to explicitly write the loop, but because using these features gives the JavaScript engine the opportunity to optimize the loop it does internally in various ways. (Whether it actually does will vary on the engine.)
...but it all amounts to looping, either explicitly or (in the case of the new array features) implicitly.
Loop and count in all browsers:
var cnt = 0;
for (var i = 0; i < arr.length; i++) {
if (arr[i] !== undefined) {
++cnt;
}
}
In modern browsers:
var cnt = 0;
arr.foreach(function(val) {
if (val !== undefined) { ++cnt; }
})
Unfortunately, No. You will you have to go through a loop and count them.
EDIT :
var arrLength = arr.filter(Number);
alert(arrLength);
If the undefined's are implicit then you can do:
var len = 0;
for (var i in arr) { len++ };
undefined's are implicit if you don't set them explicitly
//both are a[0] and a[3] are explicit undefined
var arr = [undefined, 1, 2, undefined];
arr[6] = 3;
//now arr[4] and arr[5] are implicit undefined
delete arr[1]
//now arr[1] is implicit undefined
arr[2] = undefined
//now arr[2] is explicit undefined
Remove the values then check (remove null check here if you want)
const x = A.filter(item => item !== undefined || item !== null).length
With Lodash
const x = _.size(_.filter(A, item => !_.isNil(item)))

Categories

Resources