/ 

Ref/array insert!

How to Code: The Fundamentals, pt 3

URL copied to clipboard
By AstroMacGuffin dated  last updated 

digital-identitySmall.jpg In part 2 we dealt with async/await, objects and classes. In part 3, we're getting into Arrays and a little further into objects, plus we're taking another bite of control flow. These unrelated topics are closely linked in actual use.

In this lesson, we'll cover:

  • arrays
  • how to find your way around objects and arrays
  • data type methods & built-in objects
  • comparison operators
  • for loops and the Array.forEach method
  • the Increment (++) and Decrement (--) operators
  • while loops


Data Type Methods & Built-in Objects

On a certain level, everything in JavaScript is an object. Your string variables have object methods, and even a hard-coded string has those same methods. The same is true of numbers, Arrays, and, of course, all the built-in objects you can access just by virtue of the fact that you're writing JavaScript code.

For example, Arrays have the .join() method which turns the Array into a string; and strings have the .split() method which turns the string into an Array.

And complementing those methods attached to the individual String, Number, and Array "objects" in your code, there are the Standard Built-in Objects. These objects' classes have methods that are sometimes uniquely useful, like Array.isArray() or Object.keys().

Of course MDN has a full list of all the built-ins, but it's probably more useful if I hand-pick a few: Object, Number, Math, Date, String, RegExp, Array, JSON, and Promise are the ones I use the most.

Arrays

If a variable is a box, then an Array is a box full of boxes. The internal boxes are numbered, starting with #0 for the first box. Those boxes can also be subdivided into boxes, as many layers deep as you need.

The following code sample contains a smattering of things you need to know about Arrays:

let arr = []; // create an empty array
arr[0] = 'Arrays'; // set the 1st element
arr.push('rule'); // add an element at the end
console.log( arr[1] ); // output the 2nd element
if (arr.indexOf('rule') > -1) { /* do something if 'rule' appears in array */ }
let str = arr.join(' '); // make string with spaces between elements

Finding Your Way Around Arrays

Arrays never use dot notation -- they only use square brackets. Since you can have arrays within arrays, and objects within arrays, let's go into some example cases.

const arrayOfObjects = [
  // element 0: an object with info for the 1st Doctor
  { doctor: '1st', actor: 'Hartnell' },
  // element 1: an object with info for the 9th Doctor
  { doctor: '9th', actor: 'Eccleston'}
];
console.log( arrayOfObjects[0].actor );
// outputs 'Hartnell'

Array element position numbers start at 0 and increment by 1 from there. So, arrayOfObjects[0] addresses the first element of arrayOfObjects. The first element of arrayOfObjects is an object, so we can address one of its fields by adding .actor.

Next, let's look at an array of arrays, aka a "multi-dimensional array".

const arrayOfArrays = [
  // element 0: an array of dark colors
  ['navy blue', 'forest green', 'dark grey', 'black'],
  // element 1: an array of light colors
  ['sky blue', 'bright green', 'light grey', 'white']
];
console.log( arrayOfArrays[1][3] ); 
// outputs 'white'

Arrays can contain arrays that hold arrays full of arrays, on and on, with more and more dimensions of data complexity.

Later in this lesson, you'll see how to step through arrays using two types of loops designed specifically for that job. There are a lot more than two, though!

700422main_ESA_Fomalhaut_Exoplanet_Still.png

Array Methods

There are many other methods automatically attached to every Array object you create in your code. MDN has a comprehensive guide, of course. To get you started, here are a few key Array methods.

.indexOf() and Other Search Methods

Assume you have an Array called arr and you want to know if something is in that Array, or even want to know where it is in that Array. The code arr.indexOf(thing) returns the index of thing in Array arr. When you're using .indexOf simply to test whether the thing is in the Array, remember, Arrays start at index 0, meaning a 0 is a hit. So in a conditional like that, think like this:

  • -1 is false (the thing is not in the Array),
  • greater than -1 is true (the thing is in the Array).
let arr = ['Arrays', 'rule'];
if ( arr.indexOf('rule') > -1 ) {
  // index greater than -1 = hit
}

There's also .find() which lets you specify a testing function. .find() returns the first Array entry that makes your testing function return true.

let arr = ['Arrays', 'rule'];
let match = arr.find( /* begin testing function */ (item) => { 
  // testing function code
  return item.startsWith('Array');
} );
console.log( match ); // outputs "Arrays"

The Array method .lastIndexOf() is just like .indexOf() but it starts its search at the end of the Array and steps backwards until it finds a match. It returns -1 if no match was found, or if a match was found it returns the index of the found match.

.push() and Other Insertion Methods

The .push() method inserts one or more new elements at the end of the Array.

const arr = ['Arrays', 'rule'];
arr.push('and');
arr.push('objects', 'do', 'too'); // add multiple
console.log( arr.length ); // outputs "6"

The .unshift() method adds one or more new elements at the beginning of the Array.

const arr = ['Eccleston', 'Tennant', 'Smith', 'Capaldi', 'Whittaker'];
arr.unshift(
  'Hartnell', 'Troughton', 'Pertwee', 'Baker',
  'Davison', 'Baker', 'McCoy', 'McGann'
);
console.log( arr );
/* output: 
[
  'Hartnell', 'Troughton', 'Pertwee', 'Baker', 'Davison', 'Baker', 'McCoy',
  'McGann', 'Eccleston', 'Tennant', 'Smith', 'Capaldi', 'Whittaker'
]
*/

Finally the .splice() method. This method is versatile: it deletes, it inserts, it'll chop off half the array or more… all depending on how you use it. For insertion at some arbitrary point in the Array, you need the index for the new entry (or entries). Anything currently at that position or later, will be bumped. Let's say you have the index in the variable index… you would do:

const arr = ['apple', 'banana', 'grape'];
// let's suppose a variable index === 2
arr.splice(index, 0, 'canteloupe', 'dragonfruit');
console.log( arr );
// outputs:
// [ 'apple', 'banana', 'canteloupe', 'dragonfruit', 'grape' ]

That second argument to .splice(), which is set to 0 above, is the number of elements you want to delete at that position. So you can replace, insert, do a mass delete and an insert at the same time, etc. You can also do deletions without insertions using .splice(), because the list of new entries is an optional parameter. And, you can chop off every element after index using .splice(), by leaving off the second parameter.

.join() and Other Misc Methods

The .join() method turns an Array into a string, using a string as a linker between elements. Example:

const arr = ['Arrays', 'rule', 'and', 'so', 'do', 'objects'];
console.log( arr.join(' ') );
// outputs "Arrays rule and so do objects"

The Array.isArray() static class method is how we deal with the fact that the following code outputs the string, "object":

let x = [1, 2, 3, 4, 5];
console.log( typeof x );
// outputs: "object"

(typeof will be covered later this lesson; for now just understand it outputs a string, equal to the category of whatever's to the right of the word typeof.)

So we can't use typeof to find out if something is an Array. We have to use Array.isArray(). It's not theVariable.isArray(), it's Array.isArray(theVariable), because this is a static class method rather than an object method.

The .map() method creates a new Array whose elements are generated by a callback function. You provide the callback function, which is run once per element in the Array. The callback function automatically receives inputs so long as you set the function signature to receive them. The usual signature is either (item) which gives you the element (each one, one iteration at a time), or (item, index) which gives you the element plus its index in the Array.

const arr = [1, 2, 3, 4, 5];
const a = arr.map( (item) => {
  return item*5;
});
console.log( a );
// output: [ 5, 10, 15, 20, 25 ]

The .filter() method copies a portion of an Array. You provide a function as the first (and usually only) argument to .filter(). This function gets run once for every element in the Array. The function receives input automatically, you just have to set your signature to handle those inputs. The two most commonly used signatures are (item, index) where item is the element and index is its index; or (item) -- same as before, minus the index.

const arr = [1, 2, 3, 4, 5];
const a = arr.filter( (item, index) => {
  if (index > 0 && index < 4) return true;
  else return false;
});
console.log( a );
a[0] = ' Swiss';
console.log( arr );

Interestingly, the above script didn't perform the same exact way across JavaScript implementations. For one thing, the documentation for .filter() says the copy remains linked to the source Array, and therefore changing an element in one Array, should change the other; but the two Arrays were independent in both Node.js and Opera Browser (which is a fork of Chrome, under the hood). Also, my browser assigned a[0] the value of "Swiss" before outputting the content of a; that is, the console.log() method was somehow delayed, but only in the browser. I have no explanation for this quirk and my only advice is, test often.

Moving On

Arrays do a lot more than what I've demonstrated here (we'll cover one more Array method later this lesson). Always remember you can learn more reading the MDN reference for Arrays, just as soon as you get comfortable reading its more technical format.

640px-Wide-field_view_of_the_Summer_Triangle.jpg

How to Find your Way around Objects

Just like with Arrays, it's all fantastic that an object can hold multiple values, but that does nothing for you if you don't know how to access the data inside.

Objects & Sub-Objects

Let's say you have an object like this:

const fruitColor = {
  banana: "yellow",
  apple: "red",
  pear: "green",
}

I think we all know by now that this will output red:

console.log( fruitColor.apple );

You can continue chaining dots and field names so long as necessary to address nested objects:

const fruitColor = {
  banana: "yellow",
  apple: { fuji: "red", grannySmith: "green" },
  pear: "green",
}

To output red from the above data, we now:

console.log( fruitColor.apple.fuji );

Finding Object Properties Using Variables

But what if the choice of fruit was unknown until runtime? What if you needed to use a variable to select into the fruitColor object, to identify which property you want? There is an alternate syntax for that:

let whichFruit = 'banana';
console.log( fruitColor[whichFruit] );

Since apple was broken down to fuji and grannySmith in a sub-object, dynamic addressing gets a little more involved. If we want to output red from that data:

let whichFruit = 'apple';
let whichType = 'fuji';
if (whichType)
  console.log( fruitColor[whichFruit][whichType] );
else
  console.log( fruitColor[whichFruit] );

You can mix and match dot notation vs square brackets as needed. For example, these are both the same thing:

console.log( fruitColor.apple['fuji'] );  // red
console.log( fruitColor['apple'].fuji );  // red

But this doesn't work:

console.log( fruitColor["apple.fuji"] );  // undefined

By the way, you might notice I left off the curly braces on the if/else above. Any control flow feature that involves curly braces (so, almost all of them) can be done without the curly braces -- in which case, only one statement will happen (or not happen) as a result of that control flow feature. This is illustrated in the example code above: both the if and the else each have only one line indented under them. (That indentation is only to remind the programmer about the scope of each block (or line) of code -- indentation isn't a flow control feature in JavaScript.)

A Peek Ahead

Finally, looping through objects. We're getting ahead a bit, but we'll come back to it later this lesson. You use the Object.keys() method to get an Array of the keys, and then you loop through that Array, using the values you find along the way to select into the object. Like this:

const obj = {
  ball: "round",
  seeSaw: "flat",
  truck: "complex"
};
const keys = Object.keys( obj );
keys.forEach( (item) => {
  console.log( obj[item] );
});
/* output:
round
flat
complex
*/

Comparison Operators & Other Related Operators

So far we've only discussed one of the "comparison operators" -- that being === the Strict Equality operator. But there are many operators you can use to make a conditional:

OperatorEffectExample
===returns true if values are equal and of equal type if (x === y)
==returns true if values are equal after type conversion if (x == y)
<returns true if number to left is smaller than number to right;
or if string to left comes alphabetically earlier than the string on the right
if (x < y)
>returns true if number to left is larger than number to right;
or if string to left comes alphabetically after the string on the right
if (x > y)
<=returns true if value to left of operator is less than or equal to the value on the right if (x <= y)
>=returns true if value to the left is greater than or equal to the value on the right if (x >= y)
!==returns true if the two values are not equal or don't have equal type if (x !== y)
!=returns true if the two values are not equal after type conversion if (x != y)
inreturns true if the string on the left of the operator is a key in the object to the right of the operator if ("message" in error)
typeofreturns a string describing the type of the variable after the operator if (typeof str === 'string')
instanceofreturns true if the class reference on the right, is anywhere in the ancestry of the object on the left if (obj instanceof Object)

The theme today is "space pics I have ready at hand"

for loops and the Array.forEach Method

I'm going to be honest with you, dear students. I don't know a single one of the newfangled for loops, just what I call the "traditional for loop". It looks like this:

for (let i = 0; i < something.length; i++) {
  // looped code
}

As you can see in the parentheses above, the for statement has three arguments separated by semicolons:

  • The first one happens when the loop is about to begin
  • The second one is a conditional that determines whether the loop happens, or happens again
  • The third one happens after each run of the loop

So in the example above:

  1. A variable called i is declared and defined as 0.
  2. Then i is compared to something called something.length. Arrays have a .length property that contains the number of entries in that Array. Remember, Arrays start numbering their entries at 0; this is why we're checking for i < something.length (i less than something.length) instead of i <= something.length (i less than or equal to something.length).
  3. Then the // looped code area happens (which presumably contains actual code instead of just a one-line comment).
  4. i++ happens, which adds 1 to the stored value of i.

So how does this control flow structure help you walk through an Array and address every element one by one? Because it gives you a variable (named i) that's going to loop every whole number from 0 to that Array's .length - 1 That's perfect:

// the next line creates an array
let something = ["truck", "train", "aeroplane"];
// as long as i is less than 3, keep looping
for (let i = 0; i < something.length; i++) {
  // we're in the loop now
  // something[i] is now an element of something
  // output that element
  console.log( something[i] );
}

But there's a shorter way that's great in most situations. As long as you don't need to return anything, you can:

let something = ["truck", "train", "aeroplane"];
something.forEach( (item, i) => {
  // looped code goes here
  // item is the element of something
  // i is the "index" of something
  console.log( item );
});

As shown above, Array.forEach takes a callback as an argument. The callback will be called once for each element of something. Each time the callback is called, item and i will be pre-filled with the next element of something and the index of that element. (The "index" is the number you'd put between square brackets to get that element.)

The Increment (++) and Decrement (--) Operators

You'll have noticed that the traditional for loop has i++ as the third statement in its funky signature. This is the "Increment Operator". It adds 1 to the variable it's attached to, assuming it's a number. You can subtract 1 from a variable just as easily, which for example is how you'd do a for loop that counts backwards: i--.

while Loops

Another situation you'll occasionally find yourself in that calls for the Increment Operator in a loop: you want a loop that runs until a condition is met. But you also want a limit to how many times the loop will run -- rather than strictly waiting for that condition to change. This is a job for the while loop.

let isDone = false;
let i = 0;
while ( !isDone && i < 10000 ) {
  isDone = checkStatus();
  i++;
}

We know the while loop will run at least one iteration because i starts at 0 and isDone starts as false. Why does that mean at least one iteration? Because after while is a conditional that says: evaluate as true if isDone is false or evaluates to false, and i is less than 10,000. If the conditional evaluates as true, then the loop runs. After that loop, it checks the conditional again, and, if the conditional still evaluates to true, the script runs the loop again.

We got this result out of the while conditional thanks to some new things: the "Logical Not" operator, and the "Logical And" operator (plus we used the "Less Than" comparison operator, covered earlier this lesson). Logical Not inverts whether something will evaluate as true. Logical And allows you to chain together conditional logic; the condition on the left and the condition on the right must both evaluate as true in order for the conditional to evaluate true as a whole.

Inside the loop we get the return value of some function called checkStatus() which we can assume exists. Hopefully, whoever programmed the code above, checked the return value data type first, and confirmed it eventually outputs something that evaluates as true! We store the return value of checkStatus in the variable isDone which is the same variable checked in the while conditional. Then we increment i. Here the loop ends, with control flow now hitting the while conditional to see if it's still true. If it's false, control flow skips to the next line after the while loop's closing bracket. But if the while loop's conditional evaluates as true forever, the script will keep running that loop forever, or until someone or something shuts it down.

I hope the complete lack of connection between pictures and text isn't too jarring

Lesson Conclusion

Manipulating and navigating your data is the essence of programming. Every category of software must manipulate data, and to do that, often, a coder must navigate data structures. There are often situations such that, not only do you have a complex data structure, but you have an array full of those structures. Therefore, it's important to understand loops, and know which loop is best for which job. Remember, the traditional for loop might be slightly more cumbersome to write out, but it lets you return effectively. Today's lesson also unlocked the conditional part of if and while, by exposing the conditional operators. And not last but least, I explained what the ++ and -- operators do.

πŸ”

Valid HTML!Valid CSS!Powered by Node.js!Powered by Express.js!Powered by MongoDB!