-
-
Notifications
You must be signed in to change notification settings - Fork 641
Add concept exercise objects #1160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
SleeplessByte
merged 35 commits into
exercism:main
from
junedev:add-concept-exercise-objects
Jun 23, 2021
Merged
Changes from 19 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
002b9a3
general setup
junedev 591a14c
update blurb
junedev 4c88c80
first part about.md
junedev 1bc826e
next part about.md and introduction
junedev a39c5cd
Merge branch 'main' into add-concept-exercise-objects
junedev 62c8004
sync
junedev 7fd8a43
Merge branch 'main' into add-concept-exercise-objects
junedev 992f9a4
run sync
junedev 86df5cf
add headlines and links
junedev a25d20b
Merge branch 'main' into add-concept-exercise-objects
junedev 19d2629
sync
junedev 408e741
add exercise instruction and introduction
junedev 8789207
add exemplar solution
junedev 32e8a66
add stub and tests
junedev cf35cc4
add design and hints
junedev 323311b
minor fixes
junedev ecbc761
Merge branch 'main' into add-concept-exercise-objects
junedev cf5db24
sync
junedev e425d50
format
junedev 5501205
fix typos etc
junedev bbec0f5
Merge branch 'main' into add-concept-exercise-objects
junedev 402c23b
sync
junedev 6919044
remove optional chaining
junedev 4974af3
fix typing
junedev f3aaaa0
fix punctuation
junedev 2ee1e45
Merge branch 'main' into add-concept-exercise-objects
junedev fa34c89
sync
junedev 1804cc2
Apply suggestions from code review
junedev 4fcbc17
address more review comments
junedev 744f6b1
Merge branch 'main' into add-concept-exercise-objects
junedev 692c11c
sync
junedev 7d573dc
minor addition
junedev 37ddf2e
another minor addition
junedev 83ac082
Apply suggestions from code review
junedev c9413cd
address some more review comments
junedev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"blurb": "Besides the primitive data types like \"number\" and \"string\", there is another data type in JavaScript called \"object\". Objects are collections of key-value pairs but also the fundamental building blocks of the language.", | ||
"authors": ["junedev"], | ||
"contributors": [] | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
# About | ||
|
||
## Explanation | ||
|
||
Besides the primitive data types like `number` and `string`, there is another important data type in JavaScript called `object`. | ||
Objects are collections of key-value pairs. | ||
In other languages they are referred to as maps or dictionaries and often times values need to have the same data type. | ||
In JavaScript only the type of the key is restricted, it has to be a string. | ||
The values inside one object can have different types. | ||
They can be primitive types like numbers but also arrays, other objects or even functions. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Creating an Object | ||
|
||
You create an object (literal) with curly brackets. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
You can also directly include some entries (key-value pairs). | ||
For that, write the key first followed by a colon and the value. | ||
|
||
```javascript | ||
const emptyObject = {}; | ||
|
||
const obj = { | ||
favoriteNumber: 42, | ||
greeting: 'Hello World', | ||
useGreeting: true, | ||
address: { | ||
street: 'Trincomalee Highway', | ||
city: 'Batticaloa', | ||
}, | ||
fruits: ['melon', 'papaya'], | ||
addNumbers: function (a, b) { | ||
return a + b; | ||
}, | ||
}; | ||
``` | ||
|
||
The trailing comma after the last entry is optional in JavaScript. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
You might wonder why the keys are not wrapped in quotation marks since they are supposed to be strings. | ||
This is a short-hand notation. | ||
If the key follows the naming rules for a JavaScript [identifier][mdn-identifier], you can omit the quotation marks. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
For keys with special characters in the name, you need to apply the usual string notation. | ||
|
||
```javascript | ||
const obj = { | ||
'1keyStartsWithNumber': '...', | ||
'key/with/slashes': '...', | ||
'key-with-dashes': '...', | ||
'key with spaces': '...', | ||
'#&()[]{}èä樹keyWithSpecialChars': '...', | ||
}; | ||
``` | ||
|
||
Often times you want to combine existing variables into an object. This would lead to key-value pairs like `name: name`. JavaScript has a short-hand notation that allows to just write `name` instead. There is also a short-hand notation for defining a function in an object. There you can omit the colon and the `function` keyword. | ||
|
||
```javascript | ||
const x = 1; | ||
const y = 2; | ||
|
||
const obj = { | ||
x: x, | ||
y: y, | ||
calcSum: function (a, b) { | ||
return a + b; | ||
}, | ||
}; | ||
|
||
// Can be shortened to ... | ||
const obj = { | ||
x, | ||
y, | ||
calcSum(a, b) { | ||
return a + b; | ||
}, | ||
}; | ||
``` | ||
|
||
## Retrieving a Value | ||
|
||
There are two ways to retrieve the value for a given key, dot notation and bracket notation. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```javascript | ||
const obj = { greeting: 'hello world' }; | ||
|
||
obj.greeting; | ||
// => hello world | ||
|
||
obj['greeting']; | ||
// => hello world | ||
|
||
// Bracket notation also works with variables. | ||
const key = 'greeting'; | ||
obj[key]; | ||
// => hello world | ||
``` | ||
|
||
Using the dot notation as a short-hand has the same restriction as omitting the quotation marks. | ||
It only works if the key follows the identifier naming rules. | ||
|
||
You can chain the keys if you want to retrieve a value from a nested object. | ||
|
||
```javascript | ||
const obj = { | ||
address: { | ||
street: 'Trincomalee Highway', | ||
city: 'Batticaloa', | ||
}, | ||
}; | ||
|
||
obj.address.city; | ||
// => 'Batticaloa' | ||
|
||
obj['address']['city']; | ||
// => 'Batticaloa' | ||
``` | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
If you try to retrieve a key that does not exist, JavaScript does not throw an error. [`undefined`] is returned instead. However if you were trying to retrieve a nested value and the parent key does not exist, the evaluation of the nested key is performed on `undefined` and leads to `TypeError: Cannot read property ... of undefined`. Theoretically you would always need to check the parent key exists before you can try to retrieve the nested key. To solve this problem, [optional chaining][mdn-optional-chaining] was added to the language in 2020. With the `?.` operator you can ensure that JavaScript only tries to access the nested key if the parent was not `null` or `undefined`. | ||
|
||
```javascript | ||
const obj = { | ||
address: { | ||
street: 'Trincomalee Highway', | ||
city: 'Batticaloa', | ||
}, | ||
}; | ||
|
||
obj.residence; | ||
// => undefined | ||
|
||
obj.address.zipCode; | ||
// => undefined | ||
|
||
obj.residence.street; | ||
// => TypeError: Cannot read property 'street' of undefined | ||
|
||
obj.residence?.street; | ||
// => undefined | ||
``` | ||
|
||
## Adding or Changing a Value | ||
|
||
You can add or change a value using the assignment operator `=`. | ||
Again, there is dot and bracket notation available. | ||
|
||
```javascript | ||
const obj = { greeting: 'hello world' }; | ||
|
||
obj.greeting = 'Hi there!'; | ||
obj['greeting'] = 'Welcome.'; | ||
|
||
obj.newKey1 = 'new value 1'; | ||
obj['new key 2'] = 'new value 2'; | ||
|
||
const key = 'new key 3'; | ||
obj[key] = 'new value 3'; | ||
``` | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Deleting an Entry | ||
|
||
You can delete a key-value pair from an object using the `delete` keyword. | ||
|
||
```javascript | ||
const obj = { | ||
key1: 'value1', | ||
key2: 'value2', | ||
}; | ||
|
||
delete obj.key1; | ||
delete obj['key2']; | ||
``` | ||
|
||
Note that although `undefined` is returned for non-existing keys, setting a key to `undefined` does not remove the entry. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Always use `delete` instead. | ||
|
||
You can check whether a certain key exists in an object with the `hasOwnProperty` method. | ||
|
||
```javascript | ||
const obj = { greeting: 'hello world' }; | ||
|
||
obj.hasOwnProperty('greeting'); | ||
// => true | ||
|
||
obj.hasOwnProperty('age'); | ||
// => false | ||
``` | ||
|
||
`hasOwnProperty` returns `false` for [inherited keys][concept-inheritance]. This is usually the desired behavior. | ||
If you want include inherited keys in the existence check you can use the [`in` operator][mdn-in-operator] instead. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
## Looping Through an Object | ||
|
||
There is a special `for...in` loop to iterate over all keys of an object. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```javascript | ||
const obj = { | ||
name: 'Ali', | ||
age: 65, | ||
}; | ||
|
||
for (let key in obj) { | ||
console.log(key, obj[key]); | ||
} | ||
// name Ali | ||
// age 65 | ||
``` | ||
|
||
It might seem like `for...in` always visits the keys in the order in which they appear or were added to the object (insertion order). | ||
Still you should not rely on this because there are special cases where the order will differ from the insertion order. | ||
Always write your code as if the order would be arbitrary. | ||
If you need a reliable order, use a [`Map`][jsinfo-map] or a nested [array][concept-arrays] instead of an object. | ||
|
||
The for-in loop has a pitfall in case the object has [inherited keys][concept-inheritance]. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Those will also be visited by the for-in loop. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
You can avoid this by skipping over them like shown below. | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
```javascript | ||
for (let key in obj) { | ||
if (!obj.hasOwnProperty(key)) { | ||
continue; | ||
} | ||
// ... | ||
} | ||
``` | ||
|
||
## Keys, Values and Entries | ||
|
||
The built-in object `Object` provides helper methods to retrieve all the keys, values or entries of a given object as arrays. | ||
|
||
```javascript | ||
const obj = { | ||
name: 'Ali', | ||
age: 65, | ||
}; | ||
|
||
Object.keys(obj); | ||
// => [ 'name', 'age' ] | ||
|
||
Object.values(obj); | ||
// => [ 'Ali', 65 ] | ||
|
||
Object.entries(obj); | ||
// => [ [ 'name', 'Ali' ], [ 'age', 65 ] ] | ||
``` | ||
|
||
## Truly Empty Object | ||
|
||
You might have noticed that an empty object in JavaScript is not completely empty. | ||
For example it contains the `hasOwnProperty` method and other methods like `toString`. Usually that does not cause any problems but if you ever need to create a truly empty object use a [null object][mdn-null-object] that can be created via `Object.create(null)`. | ||
|
||
[mdn-identifier]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier | ||
[mdn-shorthand-notation]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#new_notations_in_ecmascript_2015 | ||
[jsinfo-map]: https://javascript.info/map-set#map | ||
[concept-inheritance]: /tracks/javascript/concepts/inheritance | ||
[mdn-optional-chaining]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining | ||
[mdn-in-operator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in | ||
[mdn-null-object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#custom_and_null_objects |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# Introduction | ||
junedev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Besides the primitive data types like `number` and `string`, there is another important data type in JavaScript called `object`. | ||
Objects are collections of key-value pairs. | ||
In other languages they are referred to as maps or dictionaries and often times values need to have the same data type. | ||
In JavaScript only the type of the key is restricted, it has to be a string. | ||
The values inside one object can have different types. | ||
They can be primitive types like numbers but also arrays, other objects or even functions. | ||
|
||
You create an object with curly brackets. | ||
You can also directly include some entries. | ||
For that state the key first, followed by a colon and the value. | ||
|
||
```javascript | ||
const emptyObject = {}; | ||
|
||
const obj = { | ||
favoriteNumber: 42, | ||
greeting: 'Hello World', | ||
useGreeting: true, | ||
address: { | ||
street: 'Trincomalee Highway', | ||
city: 'Batticaloa', | ||
}, | ||
fruits: ['melon', 'papaya'], | ||
addNumbers: function (a, b) { | ||
return a + b; | ||
}, | ||
}; | ||
``` | ||
|
||
The trailing comma after the last entry is optional in JavaScript. | ||
|
||
You might wonder why the keys are not wrapped in quotation marks although they are supposed to be strings. | ||
This is a short-hand notation. | ||
If the key follows the naming rules for a JavaScript [identifier][mdn-identifier], you can omit the quotation marks. | ||
For keys with special characters in the name, you need to use the usual string notation. | ||
|
||
```javascript | ||
const obj = { | ||
'1keyStartsWithNumber': '...', | ||
'key/with/slashes': '...', | ||
'key-with-dashes': '...', | ||
'key with spaces': '...', | ||
'#&()[]{}èä樹keyWithSpecialChars': '...', | ||
}; | ||
``` | ||
|
||
There are two ways to retrieve the value for a given key, dot notation and bracket notation. | ||
|
||
```javascript | ||
const obj = { greeting: 'hello world' }; | ||
|
||
obj.greeting; | ||
// => hello world | ||
|
||
obj['greeting']; | ||
// => hello world | ||
|
||
// Bracket notation also works with variables. | ||
const key = 'greeting'; | ||
obj[key]; | ||
// => hello world | ||
``` | ||
|
||
Using the dot notation as a short-hand has the same restriction as omitting the quotation marks. | ||
It only works if the key follows the identifier naming rules. | ||
|
||
You can add or change a value using the assignment operator `=`. | ||
Again, there is dot and bracket notation available. | ||
|
||
```javascript | ||
const obj = { greeting: 'hello world' }; | ||
|
||
obj.greeting = 'Hi there!'; | ||
obj['greeting'] = 'Welcome.'; | ||
|
||
obj.newKey1 = 'new value 1'; | ||
obj['new key 2'] = 'new value 2'; | ||
|
||
const key = 'new key 3'; | ||
obj[key] = 'new value 3'; | ||
``` | ||
|
||
You can delete a key-value pair from an object using the `delete` keyword. | ||
|
||
```javascript | ||
const obj = { | ||
key1: 'value1', | ||
key2: 'value2', | ||
}; | ||
|
||
delete obj.key1; | ||
delete obj['key2']; | ||
``` | ||
|
||
You can check whether a certain key exists in an object with the `hasOwnProperty` method. | ||
|
||
```javascript | ||
const obj = { greeting: 'hello world' }; | ||
|
||
obj.hasOwnProperty('greeting'); | ||
// => true | ||
|
||
obj.hasOwnProperty('age'); | ||
// => false | ||
``` | ||
|
||
There is a special `for...in` loop to iterate over all keys of an object. | ||
|
||
```javascript | ||
const obj = { | ||
name: 'Ali', | ||
age: 65, | ||
}; | ||
|
||
for (let key in obj) { | ||
console.log(key, obj[key]); | ||
} | ||
// name Ali | ||
// age 65 | ||
``` | ||
|
||
To avoid subtle errors you should always assume the for-in loop visits the keys in an arbitrary order. | ||
Also be aware that `for...in` includes [inherited keys][concept-inheritance] in its iteration. | ||
|
||
[mdn-identifier]: https://developer.mozilla.org/en-US/docs/Glossary/Identifier | ||
[jsinfo-map]: https://javascript.info/map-set#map | ||
[concept-inheritance]: /tracks/javascript/concepts/inheritance |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.