What is [keys[i]] in Javascript? - javascript

I'm reading the book Understanding EMCAScript 6 and I came across a strange line that I can't decipher. result[keys[i]] = object[keys[i]];
I know that ...keys is a rest parameter. However I can access a rest parameter using standard array syntax, for example keys[i]. What does [keys[i]] mean? I googled and I can't find anything. It looks like an array but I'm not sure what type of array or how it works. Just knowing the name would be a huge help. It's from this code.
function pick(object, ...keys) {
let result = Object.create(null);
for (let i = 0, len = keys.length; i < len; i++) {
result[keys[i]] = object[keys[i]];
}
return result;
}

It's no magic, just nested property access using bracket notation.
result[ keys[i] ] = object[ keys[i] ];
could also be written
const key = keys[i];
result[key] = object[key];

To understand this function, you have to understand that, in JavaScript objects are just hashes. object.foo is just syntactic sugar for object["foo"].
This means that you can dynamically get and set object properties, which is what this function is doing.
The keys value is an array of the arguments passed to the function. Therefore, the for loop iterates over every value in keys.
The expression keys[i] means ith element of keys.
The expression object[key] means the property of the object named key. For example, if key = "foo", then writing object[key] is the same as writing object.foo.
This means that the for loop in the function:
Looks up the object property matching the given argument;
Creates a new property in result with the same name, and assigns the value of the property to it
Therefore, the pick function can be used to select certain attributes from an object:
pick({x: 1, y: 2, z: 3}, "x", "z") // -> {x: 1, z: 3}

Related

Search for Matching Properties and Equivalent Property Values

I am working on a task for freecodecamp and find myself very stuck. The task is to:
"Make a function that looks through a list (first argument) and returns an array of all objects that have equivalent property values (second argument).
Here are some helpful topics:
Global Object
Object.hasOwnProperty()
Object.keys()"
Here is what I have so far. I was hoping for some guidance and explanation not just a completely different code to copy and paste. Looking to learn and grow. Thank you so much.
function where(collection, source) {
var arr = [];
var sourceProp = Object.keys(source)[1];
for (i = 0; i<collection.length; i++){
if (collection[i].hasOwnProperty(sourceProp) && collection.sourceProp == source.sourceProp){
arr.push(collection[i]);
}
}
return arr;
}
where([{ first: 'Romeo', last: 'Montague' }, { first: 'Mercutio', last: null }, { first: 'Tybalt', last: 'Capulet' }], { last: 'Capulet' });
There are some typo mistakes in your code. See comments below:
[a] Object.keys() returns you an array of the property keys. To take the first and the only key, you should use [0], not [1].
[b] In your condition of if statement, you have to refer to the value of the i-th element by collection[i][sourceProp] where sourceProp is the key you took from step [a]. See "why" at the bottom of my answer.
[c] To take the value of source argument, you can use source[sourceProp] to access it.
function where(collection, source) {
var arr = [];
var sourceProp = Object.keys(source)[0]; // Take the first key, index=0
for (i = 0; i<collection.length; i++){
// Use collection[i][sourceProp] to take the value
if (collection[i].hasOwnProperty(sourceProp) && collection[i][sourceProp] == source[sourceProp]){
arr.push(collection[i]);
}
}
return arr;
}
NOTE: You incorrectly use dot notation to access the object's value.
When you access a variable with:
source.sourceProp
It refers to the property sourceProp of source which is not what you meant to do. To access a property with variable name, use [] notation and pass your variable in:
source[sourceProp]

Can I loop through a javascript object in reverse order?

So I have a JavaScript object like this:
foo = {
"one": "some",
"two": "thing",
"three": "else"
};
I can loop this like:
for (var i in foo) {
if (foo.hasOwnProperty(i)) {
// do something
}
}
Which will loop through the properties in the order of one > two > three.
However sometimes I need to go through in reverse order, so I would like to do the same loop, but three > two > one.
Question:
Is there an "object-reverse" function. If it was an Array, I could reverse or build a new array with unshift but I'm lost with what to do with an object, when I need to reverse-loop it's properties. Any ideas?
Thanks!
Javascript objects don't have a guaranteed inherent order, so there doesn't exist a "reverse" order.
4.3.3 Object An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive
value, object, or function. A function stored in a property of an
object is called a method.
Browsers do seem to return the properties in the same order they were added to the object, but since this is not standard, you probably shouldn't rely on this behavior.
A simple function that calls a function for each property in reverse order as that given by the browser's for..in, is this:
// f is a function that has the obj as 'this' and the property name as first parameter
function reverseForIn(obj, f) {
var arr = [];
for (var key in obj) {
// add hasOwnPropertyCheck if needed
arr.push(key);
}
for (var i=arr.length-1; i>=0; i--) {
f.call(obj, arr[i]);
}
}
//usage
reverseForIn(obj, function(key){ console.log('KEY:', key, 'VALUE:', this[key]); });
Working JsBin: http://jsbin.com/aPoBAbE/1/edit
Again i say that the order of for..in is not guaranteed, so the reverse order is not guaranteed. Use with caution!
Why there is no one has mentioned Object.keys() ?
you can get Array of Object's properties ordered as it is, then you can reverse it or filter it as you want with Array methods .
let foo = {
"one": "some",
"two": "thing",
"three": "else"
};
// Get REVERSED Array of Propirties
let properties = Object.keys(foo).reverse();
// "three"
// "two"
// "one"
// Then you could use .forEach / .map
properties.forEach(prop => console.log(`PropertyName: ${prop}, its Value: ${foo[prop]}`));
// PropertyName: three, its Value: else
// PropertyName: two, its Value: thing
// PropertyName: one, its Value: some
There is no way to loop through an object backwards, but if you recreate the object in reverse order then you are golden! Be cautions however, there is nothing that says the order of the object will stay the same as it changes and so this may lead to some interesting outcome, but for the most part it works...
function ReverseObject(Obj){
var TempArr = [];
var NewObj = [];
for (var Key in Obj){
TempArr.push(Key);
}
for (var i = TempArr.length-1; i >= 0; i--){
NewObj[TempArr[i]] = [];
}
return NewObj;
}
The just do the swap on your object like this-
MyObject = ReverseObject(MyObject);
The loop would then look like this-
for (var KeysAreNowBackwards in MyObject){
alert(MyObject[KeysAreNowBackwards]);
}
This answer is similar to a couple of the others, but some users might find the code below easier to copy-paste for their own uses:
Object.keys(foo).reverse().forEach(function(key) { console.log(foo[key]) });
For an object "foo" as described in the question, this code will output the object elements in reverse order: "else", "thing", "some"
You can use
Object.keys
One thing to take care of is that it returns a sorted list of keys (if the keys are numerical). You might need to change it accordingly.
a = {'d':'stack','l':'overflow','b':'out','a':'first','z':'empty'}
out = Object.keys(a).reverse()
console.warn("When key contains characters : "+out)
a = {'1':'stack','4':'overflow','2':'out','5':'first','3':'empty'}
out = Object.keys(a).reverse()
console.warn("When key is a numerical string : "+out)

How does for(var x in object) work if there is initially no var x in the object, initially?

I have an object, and I'm trying to see what's inside of it. So, I used print(object), which should possibly contain Spot: True, indicating that the cat Spot is alive. It returned [object object]. So, I tried, show(object), and I got Spot: True. I think that's right, but I'm not sure what the indexes are like. For example, I'm not sure if the keys are associative or numeric, or even if associative arrays are allowed in JavaScipt.
The reason I wonder why is because for (var cats in object){show(cats);} returns Spot. I can't find a way to locate the string 'cat' as being part of the array.
The cats in your example is a new variable that holds each object of iteration.
And yes, "associative arrays" are allowed, but they're really just objects:
var foo = {
bar: "baz"
}
alert(foo.bar);
alert(foo["bar"]);
Re: the for/in statement: it's more or less the same as the following, here using an array:
var cats;
var arr = [42, 69];
for (var i = 0; i < arr.length; i++) {
cats = arr[i];
alert(cats);
}
Or you can use for/in and it becomes:
for (cats in arr) {
alert(arr[cats]);
}
It's slightly different for arrays, but there's no "cats" in the array, either.
Fiddle
Javascript has arrays and objects. Arrays have numeric continuous indexes [0..length) and ordered while objects can have random indexes (strings, numbers) and are not necessarily ordered (depends on the implementation).
Using for(var key in obj) {} should only be used for objects and iterates over the properties the object has. You can use obj[var] to access the value of each property.
Note that it's useful to add an if(!obj.hasOwnProperty(key)) continue; check to the loop to ensure you do not hit properties introduced in the object's prototype.
If object is your object, I'm guessing it's not actually an array. The for (var x in object) could also be enumerating the elements (properties, functions, etc.) of your object.
Where does your object come from? What are show() and print()?
If I was trying to print out the properties of an object I might have something like this:
var myObject = {
property1: 'Test',
property2: 'Test2',
function1: function() {
// do something
}
};
for (var prop in myObject) {
if (myObject.hasOwnProperty(prop)) {
console.log(prop + ' = ' + myObject[prop]);
}
}
This should output the following:
property1 = Test
property2 = Test2
function1 = function() { // do something }
Here's a jsFiddle to show the example.
With that said, JavaScript doesn't really have associative arrays. What you can have is an object with property:value pairs, which is what I think you have.

Does Javascript have associative arrays?

Does Javascript have associative arrays? Please explain.
Nope; JavaScript arrays are just numeric keys and mixed values. The same thing can be achieved (or, actually, it's exactly the same as associative arrays in other languages) with objects:
var foo = {
a: 123,
b: 'CDE'
};
foo.a; // 123
foo['a']; // 123
You could use arrays:
var foo = [];
foo.a = 123;
foo.b = 'CDE';
foo.b; // CDE
foo['b']; // CDE
HOWEVER, this should never be done because this will not enter the key/value pairs into the array, but add them to the array object as properties. (besides, {a: 123} is easier than a = []; a.a = 123) If you need key/value pairs, use Objects. If you need an enumerated list, use arrays.
This answer is pretty much a copy-paste of my previous answer on this question.
The situation has changed in the five years since this question was asked.
Due to weak typing associative arrays can be faked in JavaScript:
>> var names = new Array();
undefined
>> names["first"] = "Dotan";
"Dotan"
>> names["last"] = "Cohen";
"Cohen"
>> for ( key in names ) { console.log(key+" "+names[key]) }
undefined
first Dotan
last Cohen
That is sometimes useful, and all browsers released since 2012 support it, but there are caveats! The array cannot be simply read back:
>> names
Array [ ]
More importantly, the array's length cannot be easily retrieved:
>> names.length
0
Therefore this is not an associative array in the sense that JavaScript would have supported it had it been intended, but rather a workaround that is often useful if for whatever reason a real JS object does not support what you need:
>> var names = {};
undefined
>> names.first = "Dotan";
"Dotan"
>> names.last = "Cohen";
"Cohen"
>> for ( key in names ) { console.log(key+" "+names[key]) }
undefined
first Dotan
last Cohen
>> names
Object { first: "Dotan", last: "Cohen" }
>> Object.keys(names).length
2
The closest we have is an object; the easiest way you can define this is using object literal syntax.
var assocArray = {
key: 1,
key2: 2
};
You should be wary of a few things however:
It does not have a .length property.
You should use for in to iterative over it, rather than for(;;;);, but should combine it with hasOwnProperty():
for (var x in assocArray) {
if (assocArray.hasOwnProperty(x)) {
// x = the key, assocArray[x] = the value
}
}
There is no concept of ordering/ sorting the members. Whilst all implementations I know of iterate the members in the order they were added, this is not standardised.
Instead of associative arrays. Javascript has objects. Properties of an object are addressed using a string.
var obj1 = {}; // declare empty object
var obj2 = {a: 1, b: 'string', c: [4,5]}; // obj with 3 properties, a, b, and c
// note that the 'c' property contains an anonymous array
alert(obj2.a); // shows 1
obj2.a = 'another string'; // redefine the 'a' property
obj2.cookie = 'oatmeal'; // add a new property to the object
obj2['ice_cream'] = {vendor: 'Beyers',
flavor: 'Chocolate Surprise'}; // add anonymous object as
// a new property for the object
assert(obj2.a === obj2['a']); // two ways to retrieve the value
var i = 'a'; // using an index varable
assert(obj2.a === obj2[i]); // note the i does not have apostrophes around it
See the Quirksmode docs
Something comparable in JavaScript is an object.
var my_obj = { key : 'value' }
Sure it does (kind of, use objects)
var foo = {
bar: "hello"
}
accessible with
foo.bar

Iterate over defined elements of a JS array

I'm using a JS array to Map IDs to actual elements, i.e. a key-value store. I would like to iterate over all elements. I tried several methods, but all have its caveats:
for (var item in map) {...}
Does iterates over all properties of the array, therefore it will include also functions and extensions to Array.prototype. For example someone dropping in the Prototype library in the future will brake existing code.
var length = map.lenth;
for (var i = 0; i < length; i++) {
var item = map[i];
...
}
does work but just like
$.each(map, function(index, item) {...});
They iterate over the whole range of indexes 0..max(id) which has horrible drawbacks:
var x = [];
x[1]=1;
x[10]=10;
$.each(x, function(i,v) {console.log(i+": "+v);});
0: undefined
1: 1
2: undefined
3: undefined
4: undefined
5: undefined
6: undefined
7: undefined
8: undefined
9: undefined
10: 10
Of course my IDs wont resemble a continuous sequence either. Moreover there can be huge gaps between them so skipping undefined in the latter case is unacceptable for performance reasons. How is it possible to safely iterate over only the defined elements of an array (in a way that works in all browsers and IE)?
Use hasOwnProperty within for ... in to make sure that prototype additions aren't included:
for (var item in map)
if (map.hasOwnProperty(item)) {
// do something
}
There are three issues:
You should not use for...in to iterate arrays.
You are using the wrong data type for your requirements.
You are not using for...in correctly.
If you want to have something like a hash table then use a plain object:
var map = {};
map[123] = 'something';
map.foo = 'bar';
// same as map['foo'] = 'bar';
//...
It looks like an array, but it is not. It is an object with property 123. You can use either dot notation obj.key (only if the key is a valid identifier - 123 would not be valid so you have to use the following notation) or array notation obj['key'] to access object properties.
It seems that an object would be a more appropriate data structure.
But even then you should make a call to hasOwnProperty (every time you use for...in):
for(var key in obj) {
if(obj.hasOwnProperty(key)) {
//do something
}
}
This checks whether a property is inherited from the prototype (it will return false then) or is truly an own property.
Use the EcmaScript 5 builtin Object.keys, and on non ES5 browsers, define it thus:
Object.keys = function (o) {
var keys = [];
var hasOwnProp = Object.prototype.hasOwnProperty;
if (Object.prototype.toString.call(o) === '[object Array]') {
for (var k in o) {
if (+k === (k & 0x7fffffff) && hasOwnProp.call(o, k)) {
keys[keys.length] = k;
}
}
keys.sort(keys, function (a, b) { return a - b; });
} else {
for (var k in o) {
if (hasOwnProp.call(o, k)) {
keys[keys.length] = k;
}
}
}
return keys;
};
1) use an object like already suggested, it is by far the best solution.
2) if you for some reason need to use an array - don't be scared looping over it with
for(var i, len = arr.length;len < i;i++)
it's very very fast.
3) don't use $.each or similar methods if you want performance - they create a new callstack for every iteration, which is a huge overhead.
Don't use an array. Use an object hash instead
var map = {};
map[key] = value;
...
for (var key in map) {
do something to map[key]
}
You can't do a lot without actually doing a check to see if the value is undefined and then doing operation a or operation b. It would be better to use a predicate to determine if the value is undefined:
x = $.grep(x, function(v, i) { return (typeof(v) != "undefined"); });
There isn't. The only way would be to omit the items from the collection completely, any solution you come up with would still have to do a test on each element for the value.
You could come up with different methods of adding the items key/value to object literals or what have you, but you would still need to omit undefined entries if you do not wish to enumerate over them.

Categories

Resources