Git Product home page Git Product logo

fewpjs-js-fundamentals-collections-nyc-web-051319's Introduction

JavaScript Fundamentals: Collections (Arrays)

Learning Goals

  • Define "data structure"
  • Describe collections and Arrays
  • Create Arrays
  • Access the elements in an Array
  • Add elements to an Array
  • Remove elements from an Array
  • Replace elements in an Array
  • Create non-destructive copies of Arrays
  • Identify nested Arrays
  • Destructure Arrays with "Spread" (...) Operator

Introduction

A data structure is a means for associating and organizing information. Outside of the programming world, we use data structures all the time. For example, we might have a shopping list of the items we need to buy on our next grocery run or an address book for organizing contact information.

If we have a lot of related data, it's best to represent it in a related system.

Describe Collections and Arrays

Previously, we have used methods such as:

document.getElementsByClassName() => [...many elements...]
document.getElementsByTagName() => [...multiple elements...]
document.getElementById() => single element

getElementById() returns a singular element whereas the other two will return all elements that match the tag or class name.

The first two methods' names hint, with the part Elements (plural), that they can return multiple matches--a collection. This is a collection that we're going to call an Array.

NOTE: These methods don't actually return Arrays. An HTMLCollection is actually returned from the first two methods. This is a major point of confusion in JavaScript! the HTMLCollection knows its length like an Array. We can iterate across it with a for...in... loop. But it's not technically an Array. This is definitely one of the "warts" of JavaScript but also shows a really mean-spirited interview question. It is not difficult to turn an HTMLCollection into an actual Array (using a loop, for example) but for our purposes we can treat it as an Array.

We can ask the DOM to give us a collection by asking it to do:

document.getElementsByTagName('p')

We can get a single member of a collection by providing an element number or, index in [] after the collection. This was done as follows:

document.getElementsByTagName('main')[0]

You've used an Array once or twice, in previous material, let's learn how to make one ourself:

Create Arrays

An Array is a list, with the items listed in a particular order, surrounded by square brackets ([]):

['This', 'is', 'an', 'array', 'of', 'strings.'];
// => ["This", "is", "an", "array", "of", "strings."]

The members or elements in an Array can be data of any type in JavaScript:

['Hello, world!', 42, null, NaN];
// => ["Hello, world!", 42, null, NaN]

NOTE: In some other languages Arrays cannot be of multiple types. In C, C++, Java, Swift, and others you cannot mix types. JavaScript, Python, Ruby, Lisp, and others permit this.

Arrays are ordered, meaning that the elements in them will always appear in the same order. The Array [1, 2, 3] is different from the Array [3, 2, 1].

Just like any other type of JavaScript data, we can assign an Array to a variable:

const primeNumbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37];

const tvShows = ['Game of Thrones', 'True Detective', 'The Good Wife', 'Empire'];

We can find out how many elements an Array contains by checking the Array's built-in length property:

const myArray = ['This', 'array', 'has', 5, 'elements'];

myArray.length;
// => 5

We defined the above Arrays using the array literal syntax — that is, we literally typed out the Array that we wanted to create, square brackets and all. There are other ways to create new Arrays, but they are only necessary for very rare circumstances. For now, use Array literals.

To get a sense of just how effective Arrays are at keeping data organized, let's store some lottery numbers an Array:

const winningNumbers = [32, 9, 14, 33, 48, 5];

The Array provides organization, and we only have to remember one identifier (winningNumbers) instead of six (firstNumber, secondNumber, and so on).

We call Arrays expressive because putting all the winning numbers in a shared data structure communicates to other programmers "Hey, these things go together" in a way that naming them individually e.g. firstNumber, secondNumber, etc. does not.

The one benefit of storing all six lottery numbers separately is that we had a really easy way to access each individual number. For example, we might want to assign powerBall to the sixth number. Luckily, Arrays offer a simple syntax for accessing individual members.

Access the Elements in an Array

Every element in an Array is assigned a unique index value that corresponds to its place within the collection. The first element in the Array is at index 0, the fifth element at index 4, and the 428th element at index 427.

To access an element, we use the computed member access operator, AKA "square brackets."

const winningNumbers = [32, 9, 14, 33, 48, 5];
// => undefined

winningNumbers[0];
// => 32

winningNumbers[3];
// => 33

NOTE: Most people just call it bracket notation or the bracket operator, so don't worry too much about remembering the term computed member access operator.

Let's take a minute to think about how we could access the last element in any Array. Remember, the one we want to make the powerBall?

If myArray contains 10 elements, the final element will be at myArray[9]. If myArray contains 15000 elements, the final element will be at myArray[14999]. So the index of the final element is always one less than the number of elements in the Array. If only we had an easy way to figure out how many elements are in the Array...

const alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
// => undefined

alphabet.length;
// => 26

alphabet[alphabet.length - 1];
// => "z"

This is why it's called the computed member access operator. We put an expression (alphabet.length - 1) inside the square brackets, and the JavaScript engine computed the value of that expression to determine which element we were trying to access. In this case, alphabet.length - 1 evaluated to 25, so alphabet[alphabet.length - 1] became alphabet[25].

While we could certainly get our lottery's powerBall with winningNumbers[5], the more flexible and less error-prone solution is:

let powerBall = winningNumbers[winningNumbers.length - 1]

Add Elements to an Array

JavaScript allows us to manipulate the members in an Array in a number of ways.

.push() and .unshift()

With the .push() method, we can add elements to the end of an Array:

const superheroes = ['Catwoman', "Miss Marvel", 'She-Hulk', 'Jessica Jones'];

superheroes.push('Wonder Woman');
// => 5

superheroes;
// => ["Catwoman", "Miss Marvel", "She-Hulk", "Jessica Jones", "Wonder Woman"]

We can also .unshift() elements onto the beginning of an Array:

const cities = ['New York', 'San Francisco'];

cities.unshift('Los Angeles');
// => 3

cities;
// => ["Los Angeles", "New York", "San Francisco"]

Notice that the value returned by both methods is the length of the updated Array.

Destructive vs. Nondestructive

Both .push() and .unshift() update or mutate the original Array, adding elements directly to it. Operations that modify the original collection are destructive, and those that leave the original collection intact are nondestructive.

Mutating the original Array isn't necessarily a bad thing, but there's also a way to add elements nondestructively, leaving the original Array intact.

Spread Operator

ES2015 introduced the spread operator, which looks like an ellipsis: .... The spread operator allows us to spread out the contents of an existing Array into a new Array, adding new elements but preserving the original:

const coolCities = ['New York', 'San Francisco'];

const allCities = ['Los Angeles', ...coolCities];

coolCities;
// => ["New York", "San Francisco"]

allCities;
// => ["Los Angeles", "New York", "San Francisco"]

We created a new Array instead of modifying the original one — our coolCities Array was untouched. We can also use the spread operator to add a new item to the end of an Array without modifying the original:

const coolCats = ['Hobbes', 'Felix', 'Tom'];

const allCats = [...coolCats, 'Garfield'];

coolCats;
// => ["Hobbes", "Felix", "Tom"]

allCats;
// => ["Hobbes", "Felix", "Tom", "Garfield"]

Remove Elements from an Array

As complements for .push() and .unshift(), respectively, we have .pop() and .shift().

.pop() and .shift()

The .pop() method removes the last element in an Array, destructively updating the original Array:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.pop();
// => "Sun"

days;
// => ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]

The .shift() method removes the first element in an Array, also mutating the original:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.shift();
// => "Mon"

days;
// => [Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

Notice that the return value for the .pop() and .shift() methods is the element that was removed.

.slice()

To remove elements from an Array nondestructively (without manipulating the original Array), we can use the .slice() method. Just as the name implies, the .slice() method returns a portion, or slice, of an Array.

With No Arguments

If we don't provide any arguments, .slice() will return a copy of the original Array with all elements intact:

const primes = [2, 3, 5, 7];

const copyOfPrimes = primes.slice();

primes;
// => [2, 3, 5, 7]

copyOfPrimes;
// => [2, 3, 5, 7]

Note that the Array returned by .slice() has the same elements as the original, but it's a copy — the two Arrays point to different objects in memory. If you add an element to one of the Arrays, it does not get added to the other:

const primes = [2, 3, 5, 7];

const copyOfPrimes = primes.slice();

primes.push(11);
// => 5

primes;
// => [2, 3, 5, 7, 11]

copyOfPrimes;
// => [2, 3, 5, 7]

With Arguments

We can provide two arguments to .slice(), the index where the slice should begin and the index before which it should end:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.slice(2, 5);
// => ["Wed", "Thu", "Fri"]

If no second argument is provided, the slice will run from the index specified by the first argument to the end of the Array:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.slice(5);
// => ["Sat", "Sun"]

To remove the first element and return a new Array, we call .slice(1):

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.slice(1);
// => ["Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

And we can remove the last element in a way that will look familiar from earlier in this lesson:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.slice(0, days.length - 1);
// => ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]

In fact, .slice() provides an easier syntax for grabbing the last element in an Array:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.slice(-1);
// => ["Sun"]

days.slice(-3);
// => ["Fri", "Sat", "Sun"]

When we provide a negative index, the JavaScript engine knows to start counting from the last element in the Array instead of the first.

DO NOT read about slice() and shrug and move on. It's used everywhere in JavaScript code and having to look up it's parameters every time is a major time-loss. Memorize its call format now.

.splice()

While .slice() allows us to return a piece of an Array without mutating the original (nondestructive), .splice() performs destructive actions. Let's familiarize ourselves with the MDN documentation:

Array.prototype.splice() documentation on MDN

The documentation shows three ways to use .splice():

array.splice(start)
array.splice(start, deleteCount)
array.splice(start, deleteCount, item1, item2, ...)

With a Single Argument

array.splice(start)

The first argument expected by .splice() is the index at which to begin the splice. If we only provide the one argument, .splice() will destructively remove a chunk of the original Array beginning at the provided index and continuing to the end of the Array:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.splice(2);
// => ["Wed", "Thu", "Fri", "Sat", "Sun"]

days;
// => ["Mon", "Tue"]

Notice that .splice() returns the removed chunk and leaves the remaining elements in the original Array.

With a negative 'start' index, the opposite happens:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
// => ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

days.splice(-2);
// => ["Sat", "Sun"]

days;
// => ["Mon", "Tue", "Wed", "Thu", "Fri"]

With Two Arguments

array.splice(start, deleteCount)

When we provide two arguments to .splice(), the first is still the index at which to begin splicing, and the second dictates how many elements we want to remove from the Array. For example, to remove 3 elements, starting with the element at index 2:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
// => ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

days.splice(2, 3);
// => ["Wed", "Thu", "Fri"]

days;
// => ["Mon", "Tue", "Sat", "Sun"]

Replace Elements in an Array

.splice() with 3+ arguments

array.splice(start, deleteCount, item1, item2, ...)

After the first two, every additional argument passed to .splice() will be inserted into the Array at the position indicated by the first argument. We can replace a single element in an Array as follows, discarding a card and drawing a new one:

const cards = ['Ace of Spades', 'Jack of Clubs', 'Nine of Clubs', 'Nine of Diamonds', 'Three of Hearts'];

cards.splice(2, 1, 'Ace of Clubs');
// => ["Nine of Clubs"]

cards;
// => ["Ace of Spades", "Jack of Clubs", "Ace of Clubs", "Nine of Diamonds", "Three of Hearts"]

Or we can remove two elements and insert three new ones as our restaurant expands its vegetarian options:

const menu = ['Jalapeno Poppers', 'Cheeseburger', 'Fish and Chips', 'French Fries', 'Onion Rings'];

menu.splice(1, 2, 'Veggie Burger', 'House Salad', 'Teriyaki Tofu');
// => ["Cheeseburger", "Fish and Chips"]

menu;
// => ["Jalapeno Poppers", "Veggie Burger", "House Salad", "Teriyaki Tofu", "French Fries", "Onion Rings"]

We aren't required to remove anything with .splice() — we can use it to insert elements anywhere within an Array. Here we're adding new books to our library in alphabetical order:

const books = ['Bleak House', 'David Copperfield', 'Our Mutual Friend'];

books.splice(2, 0, 'Great Expectations', 'Oliver Twist');
// => []

books;
// => ["Bleak House", "David Copperfield", "Great Expectations", "Oliver Twist", "Our Mutual Friend"]

Notice that .splice() returns an empty Array when we provide a second argument of 0. This makes sense because the return value is the set of elements that were removed, and we're telling it to remove 0 elements.

Keep playing around with .splice() in your browser's console to get comfortable with it.

Using the Computed Member Access Operator to Replace Elements

If we only need to replace a single element in an Array, there's a simpler solution than .splice():

const cards = ['Ace of Spades', 'Jack of Clubs', 'Nine of Clubs', 'Nine of Diamonds', 'Three of Hearts'];

cards[2] = 'Ace of Clubs';
// => "Ace of Clubs"

cards;
// => ["Ace of Spades", "Jack of Clubs", "Ace of Clubs", "Nine of Diamonds", "Three of Hearts"]

However, using the computed member access operator ([]) is still destructive — it modifies the original Array. There's a nondestructive way to replace or add items at arbitrary points within an Array, and it involves two of the concepts we learned earlier.

Slicing and Spreading

Combining .slice() and the spread operator allows us to replace elements nondestructively, leaving the original Array unharmed:

const menu = ['Jalapeno Poppers', 'Cheeseburger', 'Fish and Chips', 'French Fries', 'Onion Rings'];

const newMenu = [...menu.slice(0, 1), 'Veggie Burger', 'House Salad', 'Teriyaki Tofu', ...menu.slice(3)];

menu;
// => ["Jalapeno Poppers", "Cheeseburger", "Fish and Chips", "French Fries", "Onion Rings"]

newMenu;
// => ["Jalapeno Poppers", "Veggie Burger", "House Salad", "Teriyaki Tofu", "French Fries", "Onion Rings"]

Play around with this a bit until it makes sense. It's the trickiest thing that we've encountered in this lesson, so don't sweat it if it takes a little while to sink in!

Identify Nested Arrays

In the above 'slicing and spreading' example, if we don't use the spread operator we're left with an interesting result:

const menu = ['Jalapeno Poppers', 'Cheeseburger', 'Fish and Chips', 'French Fries', 'Onion Rings'];

const newMenu = [menu.slice(0, 1), 'Veggie Burger', 'House Salad', 'Teriyaki Tofu', menu.slice(3)];

newMenu;
// => [["Jalapeno Poppers"], "Veggie Burger", "House Salad", "Teriyaki Tofu", ["French Fries", "Onion Rings"]]

Holy nested Arrays, Batman!

Is this the array or the array within an array?

That's right — an Array can contain elements of any data type, including other Arrays:

const egregiouslyNestedArray = ['How', ['deep', ['can', ['we', ['go', ['?'], 'Pretty'], 'dang'], 'deep,'], 'it'], 'seems.'];

Pop that into your browser's JS console and check out the nesting:

egregiouslyNestedArray in the JS console

It's great that Arrays allow us to store other Arrays inside them, but this is a terrible way to represent a deeply nested data structure. In general, try to keep your Arrays to no more than two levels deep. Two levels is perfect for representing two-dimensional things like a tic-tac-toe board:

const board = [
  ['X', 'O', ' '],
  [' ', 'X', 'O'],
  ['X', ' ', 'O']
];

board;
// => [["X", "O", " "], [" ", "X", "O"], ["X", " ", "O"]]

The cool thing about representing a game board like that is in how we can access the different squares by specifying coordinates. The first [] operator grabs the row that we want, top (board[0]), middle (board[1]), or bottom (board[2]). For example:

board[1];
// => [" ", "X", "O"]

The second [] operator specifies the square within that row, left (board[1][0]), middle (board[1][1]), or right (board[1][2]). For example:

board[0][0];
// => "X"

board[0][2];
// => " "

board[2][2];
// => "O"

Effectively, we're using X and Y coordinates to refer to data within a two- dimensional structure.

Conclusion

We dove into data structures and the Array, including how to create Arrays, access elements in an Array, add elements to an Array, remove elements from an Array and replace elements in an Array. We also covered the difference between destructive and non-destructive Array manipulation.

Resources

fewpjs-js-fundamentals-collections-nyc-web-051319's People

Contributors

sgharms avatar drakeltheryuujin avatar aspenjames avatar jenmyers avatar lizbur10 avatar rrcobb avatar

Watchers

James Cloos avatar  avatar  avatar Victoria Thevenot avatar Bernard Mordan avatar Otha avatar raza jafri avatar  avatar Joe Cardarelli avatar The Learn Team avatar  avatar  avatar Matt avatar Antoin avatar  avatar Alex Griffith avatar  avatar Amanda D'Avria avatar  avatar Ahmed avatar Nicole Kroese  avatar Kaeland Chatman avatar Lisa Jiang avatar Vicki Aubin avatar Maxwell Benton avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.