Pro AngularJS - Could you help explain part of this code? - javascript

I am reading a book called Pro AngularJS by Apress and I am just trying to ensure I understand all of the code and I am a bit baffled by the following code.
Below is a custom filter in the book, which accepts 2 arguments, the data array and a property name. In the book, the property name is the category key in the data, and is set up to remove duplicate categories in order to display a list of navigation links to each category without duplication.
angular.module("customFilters", [])
.filter("unique", function () {
return function (data, propertyName) {
if (angular.isArray(data) && angular.isString(propertyName)) {
var results = [];
var keys = {};
for (var i = 0; i < data.length; i++) {
var val = data[i][propertyName];
if (angular.isUndefined(keys[val])) {
keys[val] = true;
results.push(val);
}
}
return results;
} else {
return data;
}
} });
What I don't understand is the keys part within the for loop. Keys is defined as an object literal?
Then, within the for loop, for each item in the data that us looped over, if the keys[val] is undefined (what does this mean)?
Then keys[val] is set to true (what does this do?).
I sort of understand the rest, if it is undefined, we push the result to the results array to return it.
Thanks for your help :)

Put simply, it's just a way to remember that we already have processed the value val, and do not which to return duplicates, if it comes along again in the loop.
You have to put something in the keys object, like keys[val] = true;, so that keys[val] becomes defined in the next loop iteration.
If you don't put anything into keys[val], angular.isUndefined(keys[val]) in the next loop with same value val will evaluate to true, and then your result would be duplicated (which isn't unique)
Explanation and answer to your questions
if the keys[val] is undefined (what does this mean)?
Basically means the key val doesn't exist in the object keys,
e.g. an object {'age': 45} contains the key age but doesn't contain the key weight
Then keys[val] is set to true (what does this do?)
This sets the key val of the object keys to true, so somewhere keys object looks like this {<val>: true, <other key>: ...,}
So after that step, the key val is defined for the object keys, therefore angular.isUndefined(keys[val]) condition is false
what is the purpose of keys[val] in the first place? Sorry, just not clear on what it is doing.
The code uses an object keys = {} which behaves like a key/value data structure (a dictionary, or a map, in other languages), the goal is to remember that we already have processed val
If you don't remember the values you have already processed (returned), then you will return duplicates, and therefore your unique filter will no longer return unique values, which is the purpose of the code here

Let us see the first line return function(data, propertyName)
here data is the array of objects to be filtered against propertyName(category in this case).
Then we define var keys = {} i.e, an empty object.
Now through for loop we are putting the value of propertyName(category in this case) into variable val.
So for example the first object of data array is like this [{ product: "product 1", category: 'Category 3'}, ....]
Thus the value of val = data[i][propertyName] translates to data[0][category], which evaluates to Category 3.
Now the line angular.isUndefined(keys['Category 3']) would evaluate to true for if condition (here we are asking if the given condition is undefined, which evaluates to true, thus if condition passes).
Within if loop we set the keys[val] = true, so that again this category name is not pushed to the results array.

Related

Nested object in Javascript array

JS noob here. I want to store a list of option for a dropdown in an array. I want a key to be associated with every value something like this
var newArray = [
{
key: "key",
val:({value:"val", label:"label"}, {value:"val",label:"label"})
}
]
The code above returns undefined when I try to read val. What is the solution? Thanks
var newArray = [
{
key: "key",
val:[{value:"val", label:"label"}, {value:"val",label:"label"}]
}]
The only thing i changed were parentheses () into [], because it's the thing that declares an array. Now, if you want to read the val key, you need to do this stuff.
You have an array named "newArray". It only has 1 element (which is also the first).
Now lets get the first element of the array by newArray[0]. Now you have accessed the object inside the array. Now you can read the the value by newArray[0].val. And now you have entered a new array, which elements you can read with newArray[0].val[0] and newArray[0].val[1]

"object[value] = true" meaning

i know this is kind of weird question but, i was creating a guard to prevent duplicate values in array i write some part and got a little help from stackoverflow but i can't understand code meaning properly
so i created Object with null prototype and iterated for loop over it to detect duplicate values (i know Set constructor is much easier but i am doing it in my server-side code and since older browsers does not support Set it would be dangerous to use Set). here is my code
var duplicateTagsGuard = Object.create(null);
for(var co = 0; co < tags.length; co++){
let val = tags[co];
if(val in duplicateTagsGuard){
return res.status(409).send({
message: ''
})
}
duplicateTagsGuard[val] = true
}
and the part i cant understand is duplicateTagsGuard[val] = true
so if we split my code step by step and explain it would be like
1.first create null Object
2.iterate for loop on it and declare variable val and make it equal to every element in tags array
3.then check if that val is in duplicateTagsGuard object and if it is use return statement to prevent continuing for loop and if it is not then we are adding val's value to object but i don't know how it is implemented with that part of code (duplicateTagsGuard[val] = true) if anyone can explain i will be glad
first create null Object
It is not creating a null object but it is creating an object with null as the prototype check the Object.create docs:
var duplicateTagsGuard = Object.create(null);
console.log(`duplicateTagsGuard is an empty object:`);
console.log(duplicateTagsGuard);
console.log(`Prototye of duplicateTagsGuard is null: `);
console.log(Object.getPrototypeOf(duplicateTagsGuard));
iterate for loop on it and declare variable val and make it equal to every element in tags array
This part is correct every time the loop runs a new variable is created with for block scope and is assigned the value of the current coth index value of the tags array.
then check if that val is in duplicateTagsGuard object and if it is use return statement to prevent continuing for loop and if it is not then we are adding val's value to object but i don't know how it is implemented with that part of code (duplicateTagsGuard[val] = true)
It is checking whether val is a property of the duplicateTagsGuard object, if it is already present then the return is used to return the response else it is adding that property to the duplicateTagsGuard object with the bracket notation [propertyName] and assigning it's value as true.
var duplicateTagsGuard = Object.create(null); //creating a new empty object with prototype as null
let val = "hello"; //creating a new variable
duplicateTagsGuard[val] = true; //adding the property with the value of the variable val
console.log(val in duplicateTagsGuard); //checking if the added property is present in the object
The code is creating a dictionary of val. Basically, when you iterate through the tags array, it checks whether the item in the array (accessed by tags[co]) is already present in the dictionary duplicateTagsGuard. If it has been encountered before, it will perform a certain action.
At the end of the loop, it simply injects the item into the dictionary. The dictionary therefore keep track of whether an item has been encountered before in the for loop.
The injection is done by using the item as the key in the dictionary, since it is easier to look it up (you simply have to do item in dictionary, which is basically val in duplicateTagsGuard in the actual implementation of the code). It does not matter what value you use, so a true value is used as a placeholder.

Do array elements have names by default in JavaScript? [duplicate]

This question already has answers here:
Understanding Javascript callback parameters
(3 answers)
Closed 5 years ago.
var animals = ["cat","dog","fish"];
var lengths = animals.map(function(c) {
return c.length;
});
console.log(lengths);//[3, 3, 4]
Here is the code. I don't understand where this 'c' argument comes from.
I tried to change this argument to another one (any word, actually, in both places), and the console.log result is always the same!
But this 'c' is not defined anywhere! Where does 'the engine' get the value of this 'c'?
You've asked two slightly different questions. First to the question body:
I don't understand where this 'c' argument comes from. I tried to change this argument to another (any word, actually, in both places), and the console.log result is always the same!
But this 'c' is not defined anywhere!
Where does 'the engine' gets the value of this 'c'?
You define the parameter name (as you've noticed, you can choose any name for it you like). The value comes from the array, because map calls your callback and determines what argument to pass for that parameter.
Here's a conceptual implementaton of Array.prototype.map, which make make this clearer:
// CONCEPTUAL ONLY, NOT AN ACTUAL VERSION OF IT
function maplike(array, callback) {
var result = [];
for (var i = 0; i < array.length; ++i) {
result[i] = callback(array[i]);
// ^^^^^^^^--- where 'c' comes from
}
return result;
}
var animals = ["cat","dog","fish"];
var lengths = maplike(animals, function(c) {
return c.length;
});
console.log(lengths);//[3, 3, 4]
Do array elements have names by default in JavaScript?
Sort of, but not in the way you're thinking. The name of the element is its index, 0, 1, etc. In fact, JavaScript arrays aren't really arrays at all* and those indexes are converted to string property names (in theory; in practice, JavaScript engines optimize it).
* (disclosure: that's a post on my anemic little blog)
You're telling the interpreter how the parameter is called, here:
function(c) {
^
Array.prototype.map() requires a callback that accepts up to 3 parameters. The first parameter is always the "current item", which you happen to have named c.
For a more in-depth explanation, have a look at T.J. Crowders answer, as well.
In javascript, functions are first class object, which means they can be assigned to variables, passed as function parameters and returned from values. The Array.prototype.map function takes a function with it's first parameter denoting an item of the array. When invoked, the map function executes the given function for each of the items and creates a new array from the outputs of the given function.
In your case, you are defining the input function on the fly, inside the map function.
You can actually define the function outside and pass the function by reference inside map like below.
function getLength(item) {
return item.length;
}
var animals = ["cat","dog","fish"];
var lengths = animals.map(getLength);
console.log(lengths);//[3, 3, 4]
Here, you can see it outputs the same result.
The code does not know what is the parameter named. You map an array. map function creates a new array in the lengths variable (variable being assigned to). How? It provides to the function parameter inside it, each element in the current array one-by-one by value.
Here the value is actual string name ("cat" or "dog" or "fish").
In javascript, parameters can be optional. This map function can take three parameters, currentValue, index, array. In your case, c provides currentvalue.
If you would add one more parameter c,idx. Map function will get currentvalue and index inside it.
var animals = ["cat","dog","fish"];
var lengths = animals.map(function(c, idx, arr, test) {
console.log(c); // currentvalue being processed in the array.
console.log(idx); // index of currentvalue in the array
console.log(arr); // original array being operated on.
console.log(test); // undefined always. not available in map.
return c.length;
});
console.log(lengths);//[3, 3, 4]

Copy key:value to object elements of an array Javascript

This is a simple test of javascript knowledge that I seem to be lacking. I'm basically writing a query front end for monogdb, it's almost done apart from one little piece. Queries are built by clicking elements and most of the logic works. I'm stuck on building an $and query.
I have a flat object like this:
obj1: {
prop1: 'val1'
}
These key values are added dynamically when the user clicks on a list-item and enters a custom value. When the user clicks 2 or more items from the list-items I would like for my object to end up like this:
obj1: {
$and: [
{prop1: 'val1'},
{prop2: 'val2'},
{prop3: 'val3'}
]
}
Where all the original key value strings are wrapped in {} and become elements to an $and array.
I can get the first part working i.e. turning the key values into objects. And I can get the second part, injecting an $and array and feeding it some kind of object but I can't get the two to work together consistently.
It requires taking the value already in obj1, making an object out of it and pushing it into an array. Injecting that array into obj1, and then removing the old key value from obj1.
This is as far as I've got:
var add = []
var newElementObject= {}
for (var prop in obj1) {
newElementObject = {};
newElementObject[prop] = "custom value entered by user";
delete obj1[prop]
add.push(newElementObject);
}
obj1.$and = add;
It works, once. I get the structure I'm looking for but when I try to add more items to the $and array something craps out. The second obj updates and the first just sits there saying:
{$and : "custom value entered by user"}
I've been staring at this for too long.
Try
var newElementObject = {};
obj1.$add = obj1.$add || []; //keep the old array or make a new one
for (var prop in obj1) {
if (!obj1.hasOwnProperty(prop) || prop == '$add') { continue; }
newElementObject = {};
newElementObject[prop] = "custom value entered by user";
delete obj1[prop]
obj1.$add.push(newElementObject);
}
That way you're not going to overwrite the old array reference if you run this code twice on the same object, which is what it sounds like your problem is.

How is this code getting rid of duplicate array elements?

I've been teaching myself OOJS by creating a blackjack game. Everything is going great, but since I randomly create cards, sometimes I create and then deal the same card twice in a round. I'm trying to avoid that by writing some logic that gets rid of duplicate cards.
So I found this discussion.
https://stackoverflow.com/a/840849/945517
and the code below:
function eliminateDuplicates(arr) {
var i,
len=arr.length,
out=[],
obj={};
for (i=0;i<len;i++) {
obj[arr[i]]=0;
}
for (i in obj) {
out.push(i);
}
return out;
}
var a=[];
var b=[];
a[0]="fish";
a[1]="fishes";
a[2]="fish";
a[3]="1";
a[4]="fishes";
b=eliminateDuplicates(a);
console.log(a);
console.log(b);
I understand what's going on in general and on almost every line except:
for (i=0;i<len;i++) {
obj[arr[i]]=0;
}
It seems like it's looping through the array and setting the key of obj to zero. What's going on here and how does this help get rid of duplicate entries in the array being passed at the start?
Thanks!
{} = {key: value, key2:value2,....}
The above is essentially a key value map. The code iterates through the array and adds the key into the map with a value of zero. When the map tries to access an existing value, all it does is reset the value to zero. A useless operation, but it avoids an if..else or other more complex logic.
Once the array is done being iterated across, you only need to iterate across the key value map to get the keys. Since the keys are guaranteed to be unique, you have a list of unique items.
The main thing to realize here is that an object can only have one property per unique string, so when you set obj.fish to 0 when i = 0, you don't add a second "fish" property to obj when i = 2.
Then you can just loop through all of the properties of obj, each one is guaranteed to be unique, and thus you have stripped duplicates.

Categories