mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-04-24 01:00:51 -04:00
feat(curriculum): add workshop heritage library catalog (#66022)
Co-authored-by: Dario <105294544+Dario-DC@users.noreply.github.com> Co-authored-by: majestic-owl448 <26656284+majestic-owl448@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
a60ccdb293
commit
7aeefcb677
@@ -4872,6 +4872,13 @@
|
||||
"In this workshop, you'll leverage JavaScript loops to build a space mission roster."
|
||||
]
|
||||
},
|
||||
"workshop-heritage-library-catalog": {
|
||||
"title": "Build a Heritage Library Catalog",
|
||||
"intro": [
|
||||
"In this workshop, you will digitize historical catalog cards for a heritage library.",
|
||||
"You will practice using loops, objects, and string methods to parse raw text data, search and group entries, render formatted output, and export to JSON and CSV."
|
||||
]
|
||||
},
|
||||
"lab-longest-word-in-a-string": {
|
||||
"title": "Build a Longest Word Finder App",
|
||||
"intro": [
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
id: 699d1f8e51fcdc8230f779fb
|
||||
title: Step 1
|
||||
challengeType: 1
|
||||
dashedName: step-1
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In this workshop, you will build a heritage library catalog system. You will parse raw book data, search and group entries, validate data quality, and export results to different formats.
|
||||
|
||||
Each catalog card is stored as a single string in the format `"Title | Author | Year | Location"`, with fields separated by the pipe character (`|`).
|
||||
|
||||
You will use an array of these strings throughout the workshop to build and test your catalog functions. Create an array named `rawCatalogCards` and add the following two strings to it:
|
||||
|
||||
```js
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1"
|
||||
```
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a variable named `rawCatalogCards`.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /(const|let|var)\s+rawCatalogCards\s*=/);
|
||||
```
|
||||
|
||||
`rawCatalogCards` should be an array.
|
||||
|
||||
```js
|
||||
assert.isArray(rawCatalogCards);
|
||||
```
|
||||
|
||||
The first element of `rawCatalogCards` should be `"From a Buick 8 | King, Stephen | 2002 | Shelf K7"`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(rawCatalogCards[0], "From a Buick 8 | King, Stephen | 2002 | Shelf K7");
|
||||
```
|
||||
|
||||
The second element of `rawCatalogCards` should be `"The Shining | King, Stephen | 1977 | Shelf K1"`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(rawCatalogCards[1], "The Shining | King, Stephen | 1977 | Shelf K1");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee885450
|
||||
title: Step 2
|
||||
challengeType: 1
|
||||
dashedName: step-2
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The rest of the catalog cards have been added. Notice the three entries near the end with missing fields; you will handle those in a later step.
|
||||
|
||||
`parseCard` will take a raw catalog string and return a structured object with four properties: `title`, `author`, `year`, and `location`. For now, create a function `parseCard` with a parameter named `rawString`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a function named `parseCard`.
|
||||
|
||||
```js
|
||||
assert.isFunction(parseCard);
|
||||
```
|
||||
|
||||
`parseCard` should be a function with a parameter named `rawString`.
|
||||
|
||||
```js
|
||||
const regex = __helpers.functionRegex('parseCard', ['rawString']);
|
||||
assert.match(__helpers.removeJSComments(code), regex);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee885451
|
||||
title: Step 3
|
||||
challengeType: 1
|
||||
dashedName: step-3
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `parseCard` function will take a raw catalog string and return a structured object. Before building out your function, set up a way to see its output as you work.
|
||||
|
||||
Call `parseCard` with `rawCatalogCards[2]` as the argument, assign the result to a variable named `cardResult`, and log `cardResult` to the console.
|
||||
|
||||
Right now `parseCard` has no body, so it returns `undefined`. As you add code to the function over the next steps, the logged output will show you exactly what the function is returning at each stage.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should call `parseCard` with `rawCatalogCards[2]` as the argument.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /parseCard\s*\(\s*rawCatalogCards\s*\[\s*2\s*\]\s*\)/);
|
||||
```
|
||||
|
||||
You should assign the result of calling `parseCard` with `rawCatalogCards[2]` to a variable named `cardResult`.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /(const|let|var)\s+cardResult\s*=\s*parseCard\s*\(\s*rawCatalogCards\s*\[\s*2\s*\]\s*\)\s*;?/);
|
||||
```
|
||||
|
||||
You should log `cardResult` to the console.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*cardResult\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {}
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee885454
|
||||
title: Step 4
|
||||
challengeType: 1
|
||||
dashedName: step-4
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The first step in parsing a catalog card is separating the four fields. The `.split()` method breaks a string into an array of substrings at every occurrence of the separator you provide.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1".split("|")
|
||||
// ["The Shining ", " King, Stephen ", " 1977 ", " Shelf K1"]
|
||||
```
|
||||
|
||||
Inside `parseCard`, call `.split("|")` on `rawString` and return the result. After this step, the console should show an array of four strings.
|
||||
|
||||
**Note:** You may notice extra whitespace around the values. You will clean that up in the next step.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should call `.split("|")` on `rawString` within your `parseCard` function.
|
||||
|
||||
```js
|
||||
assert.match(parseCard.toString(), /rawString\s*\.\s*split\s*\(\s*('|"|`)\|\1\s*\)\s*;?/);
|
||||
```
|
||||
|
||||
`parseCard` should return an array.
|
||||
|
||||
```js
|
||||
assert.isArray(parseCard("A | B | C | D"));
|
||||
```
|
||||
|
||||
When called with `"A | B | C | D"`, `parseCard` should return an array with four elements.
|
||||
|
||||
```js
|
||||
assert.strictEqual(parseCard("A | B | C | D").length, 4);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
}
|
||||
|
||||
const cardResult = parseCard(rawCatalogCards[2]);
|
||||
console.log(cardResult);
|
||||
```
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee885455
|
||||
title: Step 5
|
||||
challengeType: 1
|
||||
dashedName: step-5
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
As you can see from the output in the console, after splitting, each part still has extra leading and/or trailing whitespace. To clean every element, you need to process each one individually.
|
||||
|
||||
This is a perfect use case for a `for` loop, which repeats a block of code once for each element in an iterable, such as an array:
|
||||
|
||||
```js
|
||||
for (let i = 0; i < array.length; i++) {
|
||||
// code to run on each iteration
|
||||
}
|
||||
```
|
||||
|
||||
`i` starts at `0` (the first index), the loop continues while `i` is less than the array's length, and `i++` increases `i` by one after each iteration.
|
||||
|
||||
Inside `parseCard`, declare an empty array named `trimmedParts`. Then add a `for` loop that iterates over `parts`. Leave the loop body empty for now, as you'll fill it in the next step.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a variable named `trimmedParts` set to an empty array.
|
||||
|
||||
```js
|
||||
assert.match(parseCard.toString(), /(const|let|var)\s+trimmedParts\s*=\s*\[\s*\]/);
|
||||
```
|
||||
|
||||
You should use a `for` loop to iterate over `parts`.
|
||||
|
||||
```js
|
||||
assert.match(parseCard.toString(), /for\s*\(\s*(var|let)\s+(\w+)\s*=\s*0\s*;\s*\2\s*<\s*parts\s*\.\s*length\s*;\s*\2\+\+\s*\)\s*\{/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
return parts;
|
||||
}
|
||||
|
||||
const cardResult = parseCard(rawCatalogCards[2]);
|
||||
console.log(cardResult);
|
||||
```
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee885456
|
||||
title: Step 7
|
||||
challengeType: 1
|
||||
dashedName: step-7
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that the parts are clean, you can transform them into a structured object. In JavaScript, objects use curly braces with "key: value" pairs. For example, `{ title: "Dune", year: 1965 }`.
|
||||
|
||||
Declare four variables: `title`, `author`, `year`, and `location`. Assign to each of them the corresponding element from `trimmedParts`. Then update the `return` statement to return an object with those four properties.
|
||||
|
||||
# --hints--
|
||||
|
||||
`parseCard` should return an object with `title`, `author`, `year`, and `location` properties.
|
||||
|
||||
```js
|
||||
const result = parseCard("The Stand | King, Stephen | 1978 | Shelf K2");
|
||||
assert.isObject(result);
|
||||
assert.property(result, "title");
|
||||
assert.property(result, "author");
|
||||
assert.property(result, "year");
|
||||
assert.property(result, "location");
|
||||
```
|
||||
|
||||
The `title` property of the object returned by `parseCard("The Stand | King, Stephen | 1978 | Shelf K2")` should have the value `"The Stand"`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(parseCard("The Stand | King, Stephen | 1978 | Shelf K2").title, "The Stand");
|
||||
```
|
||||
|
||||
The `author` property of the object returned by `parseCard("The Stand | King, Stephen | 1978 | Shelf K2")` should have the value `"King, Stephen"`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(parseCard("The Stand | King, Stephen | 1978 | Shelf K2").author, "King, Stephen");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
return trimmedParts;
|
||||
--fcc-editable-region--
|
||||
}
|
||||
|
||||
const cardResult = parseCard(rawCatalogCards[2]);
|
||||
console.log(cardResult);
|
||||
```
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee885457
|
||||
title: Step 8
|
||||
challengeType: 1
|
||||
dashedName: step-8
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Some catalog cards have missing fields. For example, entry 16 has a blank author, so `parseCard` currently returns an empty string for that field.
|
||||
|
||||
The `||` operator provides a fallback: `a || b` evaluates to `a` when `a` is truthy, and `b` otherwise. Since an empty string is falsy, `"" || "Unknown"` evaluates to `"Unknown"`.
|
||||
|
||||
Update `title`, `author`, and `location` in the return statement to use `|| "Unknown"` as a fallback. Leave `year` for the next step.
|
||||
|
||||
# --hints--
|
||||
|
||||
When called with a string that has a blank author field, the `author` property of the returned object should be `"Unknown"`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(parseCard("X | | 2000 | Y").author, "Unknown");
|
||||
```
|
||||
|
||||
When called with a string that has a blank title field, the `title` property of the returned object should be `"Unknown"`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(parseCard(" | A | 2000 | Y").title, "Unknown");
|
||||
```
|
||||
|
||||
When called with a string that has a blank location field, the `location` property of the returned object should be `"Unknown"`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(parseCard("X | A | 2000 | ").location, "Unknown");
|
||||
```
|
||||
|
||||
You should not add a fallback for `year` yet.
|
||||
|
||||
```js
|
||||
assert.strictEqual(parseCard("X | A | | Y").year, "");
|
||||
```
|
||||
|
||||
When all fields are present, `parseCard` should return their actual values and not `"Unknown"`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(parseCard("The Stand | King, Stephen | 1978 | Shelf K2").title, "The Stand");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
--fcc-editable-region--
|
||||
return {
|
||||
title: title,
|
||||
author: author,
|
||||
year: year,
|
||||
location: location
|
||||
};
|
||||
--fcc-editable-region--
|
||||
}
|
||||
|
||||
const cardResult = parseCard(rawCatalogCards[2]);
|
||||
console.log(cardResult);
|
||||
```
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee885458
|
||||
title: Step 9
|
||||
challengeType: 1
|
||||
dashedName: step-9
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The year field arrives as a string, but you need it as a number for a later calculation. Use `parseInt()` to convert it.
|
||||
|
||||
The year can also be blank, so you need a fallback. The ternary operator `condition ? valueIfTrue : valueIfFalse` returns one of two values based on a condition.
|
||||
|
||||
Update the `year` property in the return statement to:
|
||||
|
||||
```js
|
||||
year: year ? parseInt(year) : "Unknown"
|
||||
```
|
||||
|
||||
# --hints--
|
||||
|
||||
When called with a card that has a valid year, the `year` property of the returned object should be a number, not a string.
|
||||
|
||||
```js
|
||||
assert.isNumber(parseCard("The Shining | King, Stephen | 1977 | Shelf K1").year);
|
||||
```
|
||||
|
||||
When called with `"The Shining | King, Stephen | 1977 | Shelf K1"`, the `year` property should be `1977`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(parseCard("The Shining | King, Stephen | 1977 | Shelf K1").year, 1977);
|
||||
```
|
||||
|
||||
When called with a string that has a blank year field, the `year` property should be `"Unknown"`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(parseCard("X | A | | Y").year, "Unknown");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
--fcc-editable-region--
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year,
|
||||
location: location || "Unknown"
|
||||
};
|
||||
--fcc-editable-region--
|
||||
}
|
||||
|
||||
const cardResult = parseCard(rawCatalogCards[2]);
|
||||
console.log(cardResult);
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee885459
|
||||
title: Step 10
|
||||
challengeType: 1
|
||||
dashedName: step-10
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`parseCard` has been tested and works correctly. The `cardResult` variable is no longer needed.
|
||||
|
||||
Start by removing the `const cardResult` line and `console.log(cardResult)` line. Then create a function named `parseCatalog` that takes `rawCards` as a parameter. For now, declare an empty array named `catalog` inside the function and return it.
|
||||
|
||||
Below the function, call `parseCatalog(rawCatalogCards)`, assign the result to a variable named `catalog`, and log `catalog.length`.
|
||||
|
||||
# --hints--
|
||||
|
||||
`parseCatalog` should be a function that returns an array.
|
||||
|
||||
```js
|
||||
assert.isArray(parseCatalog(rawCatalogCards));
|
||||
```
|
||||
|
||||
`catalog` should be defined and set to `parseCatalog(rawCatalogCards)`.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /(const|let|var)\s+catalog\s*=\s*parseCatalog\s*\(/);
|
||||
```
|
||||
|
||||
You should delete the `cardResult` variable and the related `console.log()` call.
|
||||
|
||||
```js
|
||||
assert.notMatch(__helpers.removeJSComments(code), /\bcardResult\b/);
|
||||
```
|
||||
|
||||
You should log `catalog.length` to the console.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*catalog\.length\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const cardResult = parseCard(rawCatalogCards[2]);
|
||||
console.log(cardResult);
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee88545a
|
||||
title: Step 12
|
||||
challengeType: 1
|
||||
dashedName: step-12
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
With the catalog parsed, you can search it by field. `findByAuthor` will filter entries whose author contains a search term.
|
||||
|
||||
Create a function named `findByAuthor` that takes `catalog` and `author` as parameters. Inside, declare a variable named `searchTerm` and set it to the lowercase version of `author` to enable case-insensitive matching later.
|
||||
|
||||
Also declare a variable named `results` and assign it an empty array to hold the matches.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a function named `findByAuthor` with `catalog` and `author` as parameters.
|
||||
|
||||
```js
|
||||
const regex = __helpers.functionRegex('findByAuthor', ['catalog', 'author']);
|
||||
assert.match(__helpers.removeJSComments(code), regex);
|
||||
```
|
||||
|
||||
You should declare a variable named `searchTerm` set to `author.toLowerCase()`.
|
||||
|
||||
```js
|
||||
assert.match(findByAuthor.toString(), /searchTerm\s*=\s*author\.toLowerCase\s*\(\s*\)/);
|
||||
```
|
||||
|
||||
You should declare a variable named `results` set to an empty array.
|
||||
|
||||
```js
|
||||
assert.match(findByAuthor.toString(), /(const|let|var)\s+results\s*=\s*\[\s*\]/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee88545b
|
||||
title: Step 15
|
||||
challengeType: 1
|
||||
dashedName: step-15
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Let's verify the search works. Stephen King has six books in the catalog.
|
||||
|
||||
Call `findByAuthor(catalog, "king")` and assign the result to a variable named `kingBooks`. Log `kingBooks.length` to confirm the count.
|
||||
|
||||
Then use a `for` loop to log each result. Use a template literal to include the title and year, like `` `${kingBooks[i].title} (${kingBooks[i].year})` ``.
|
||||
|
||||
# --hints--
|
||||
|
||||
`kingBooks` should have a length of `6`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(kingBooks.length, 6);
|
||||
```
|
||||
|
||||
You should log `kingBooks.length` to the console.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*kingBooks\.length\s*\)/);
|
||||
```
|
||||
|
||||
You should use a `for` loop to log each book's title and year.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /for\s*\(\s*(var|let)\s+(\w+)\s*=\s*0\s*;\s*\2\s*<\s*kingBooks\s*\.\s*length\s*;\s*\2\+\+\s*\)\s*\{/);
|
||||
assert.match(__helpers.removeJSComments(code), /kingBooks\[.+\]\.title\b/);
|
||||
assert.match(__helpers.removeJSComments(code), /kingBooks\[.+\]\.year\b/);
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,111 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee88545c
|
||||
title: Step 17
|
||||
challengeType: 1
|
||||
dashedName: step-17
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now you'll build `groupByDecade`, which organizes the catalog by decade. Each key will be a string like `"1970s"` and each value an array of books from that period.
|
||||
|
||||
Create a function named `groupByDecade` that takes `catalog` as a parameter. Inside, declare `const grouped = {}`. Add a `for` loop to iterate over `catalog`, and inside the loop declare `const book = catalog[i]`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should have a function named `groupByDecade` with a `catalog` parameter.
|
||||
|
||||
```js
|
||||
const regex = __helpers.functionRegex('groupByDecade', ['catalog']);
|
||||
assert.match(__helpers.removeJSComments(code), regex);
|
||||
```
|
||||
|
||||
You should declare a variable named `grouped` set to an empty object.
|
||||
|
||||
```js
|
||||
assert.match(groupByDecade.toString(), /(const|let|var)\s+grouped\s*=\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
You should use a `for` loop to iterate over `catalog`.
|
||||
|
||||
```js
|
||||
assert.match(groupByDecade.toString(), /for\s*\(\s*(var|let)\s+(\w+)\s*=\s*0\s*;\s*\2\s*<\s*catalog\s*\.\s*length\s*;\s*\2\+\+\s*\)\s*\{/);
|
||||
```
|
||||
|
||||
You should declare a variable named `book` set to the current catalog entry.
|
||||
|
||||
```js
|
||||
assert.match(groupByDecade.toString(), /(const|let|var)\s+book\s*=\s*catalog\[/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,127 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee88545d
|
||||
title: Step 19
|
||||
challengeType: 1
|
||||
dashedName: step-19
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
After the `"Unknown"` check, add the decade-grouping logic for books with numeric years. Declare `const decade = Math.floor(book.year / 10) * 10`. For example, `Math.floor(1977 / 10)` gives `197`, times `10` gives `1970`.
|
||||
|
||||
Then declare `const decadeKey =` `` `${decade}s` `` to produce a string like `"1970s"`.
|
||||
|
||||
Use bracket notation to group the book: check if `grouped[decadeKey]` doesn't exist yet and initialize it as an empty array, then push `book` into it. Bracket notation is required here because the key is stored in a variable.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a variable named `decade` set to `Math.floor(book.year / 10) * 10`.
|
||||
|
||||
```js
|
||||
assert.match(groupByDecade.toString(), /(const|let|var)\s+decade\s*=\s*Math\.floor\s*\(\s*book\.year\s*\/\s*10\s*\)\s*\*\s*10/);
|
||||
```
|
||||
|
||||
You should declare a variable named `decadeKey` that includes `decade` and the letter `"s"`.
|
||||
|
||||
```js
|
||||
assert.match(groupByDecade.toString(), /(const|let|var)\s+decadeKey\s*=.*decade/s);
|
||||
assert.match(groupByDecade.toString(), /decadeKey.*s/s);
|
||||
```
|
||||
|
||||
You should check if `grouped[decadeKey]` doesn't exist yet and initialize it as an empty array.
|
||||
|
||||
```js
|
||||
assert.match(groupByDecade.toString(), /!\s*grouped\s*\[\s*decadeKey\s*\]/);
|
||||
assert.match(groupByDecade.toString(), /grouped\s*\[\s*decadeKey\s*\]\s*=\s*\[\s*\]/);
|
||||
```
|
||||
|
||||
You should push `book` into `grouped[decadeKey]`.
|
||||
|
||||
```js
|
||||
assert.match(groupByDecade.toString(), /grouped\s*\[\s*decadeKey\s*\]\s*\.push\s*\(\s*book\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,135 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee88545e
|
||||
title: Step 20
|
||||
challengeType: 1
|
||||
dashedName: step-20
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The loop is complete. Return `grouped` from the function.
|
||||
|
||||
Then, below the function, call `groupByDecade(catalog)` and assign the result to a variable named `byDecade`. Log `byDecade` to inspect the grouped result, and you should see an object with keys like `"1950s"`, `"1960s"`, `"1970s"`, and so on, each pointing to an array of books from that decade.
|
||||
|
||||
# --hints--
|
||||
|
||||
`groupByDecade` should return an object.
|
||||
|
||||
```js
|
||||
assert.isObject(groupByDecade(catalog));
|
||||
```
|
||||
|
||||
`byDecade["1980s"].length` should be `5`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(groupByDecade(catalog)["1980s"].length, 5);
|
||||
```
|
||||
|
||||
`byDecade["Unknown"].length` should be `1`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(groupByDecade(catalog)["Unknown"].length, 1);
|
||||
```
|
||||
|
||||
You should assign the result of `groupByDecade(catalog)` to a variable named `byDecade`.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /(const|let|var)\s+byDecade\s*=\s*groupByDecade\s*\(\s*catalog\s*\)/);
|
||||
```
|
||||
|
||||
You should log `byDecade` to the console.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*byDecade\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
const decade = Math.floor(book.year / 10) * 10;
|
||||
const decadeKey = `${decade}s`;
|
||||
if (!grouped[decadeKey]) {
|
||||
grouped[decadeKey] = [];
|
||||
}
|
||||
grouped[decadeKey].push(book);
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,133 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee88545f
|
||||
title: Step 21
|
||||
challengeType: 1
|
||||
dashedName: step-21
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `byDecade` log has served its purpose. Remove `console.log(byDecade)` from your code.
|
||||
|
||||
Then, to make catalog entries easier to scan, create a function named `renderEntry` that takes `entry` as a parameter. Leave the function body empty for now. Below the function, log `renderEntry(catalog[0])`.
|
||||
|
||||
# --hints--
|
||||
|
||||
`renderEntry` should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(renderEntry);
|
||||
```
|
||||
|
||||
`renderEntry` should be a function with an `entry` parameter.
|
||||
|
||||
```js
|
||||
const regex = __helpers.functionRegex('renderEntry', ['entry']);
|
||||
assert.match(__helpers.removeJSComments(code), regex);
|
||||
```
|
||||
|
||||
The `console.log(byDecade)` call should be removed.
|
||||
|
||||
```js
|
||||
assert.notMatch(__helpers.removeJSComments(code), /console\.log\s*\(\s*byDecade\s*\)/);
|
||||
```
|
||||
|
||||
You should log `renderEntry(catalog[0])`.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*renderEntry\s*\(\s*catalog\s*\[\s*0\s*\]\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
const decade = Math.floor(book.year / 10) * 10;
|
||||
const decadeKey = `${decade}s`;
|
||||
if (!grouped[decadeKey]) {
|
||||
grouped[decadeKey] = [];
|
||||
}
|
||||
grouped[decadeKey].push(book);
|
||||
}
|
||||
return grouped;
|
||||
}
|
||||
|
||||
const byDecade = groupByDecade(catalog);
|
||||
--fcc-editable-region--
|
||||
console.log(byDecade);
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,158 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee885460
|
||||
title: Step 23
|
||||
challengeType: 1
|
||||
dashedName: step-23
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Before trusting parsed data, start by validating required fields.
|
||||
|
||||
Create a function named `validateEntry` that takes `entry` as a parameter. Declare `let isValid = true`.
|
||||
|
||||
Add a validation check for `title`: set `isValid = false` if `title` is missing, falsy, or equals `"Unknown"`.
|
||||
|
||||
Return `isValid`. You will add checks for the other fields in the next steps.
|
||||
|
||||
# --hints--
|
||||
|
||||
`validateEntry` should return a boolean.
|
||||
|
||||
```js
|
||||
assert.isBoolean(validateEntry({ title: "T", author: "A", year: 2000, location: "L" }));
|
||||
```
|
||||
|
||||
`validateEntry({ title: "T", author: "A", year: 2000, location: "L" })` should return `true`.
|
||||
|
||||
```js
|
||||
assert.isTrue(validateEntry({ title: "T", author: "A", year: 2000, location: "L" }));
|
||||
```
|
||||
|
||||
`validateEntry({})` should return `false`.
|
||||
|
||||
```js
|
||||
assert.isFalse(validateEntry({}));
|
||||
```
|
||||
|
||||
`validateEntry({ title: "", author: "A", year: 2000, location: "L" })` should return `false`.
|
||||
|
||||
```js
|
||||
assert.isFalse(validateEntry({ title: "", author: "A", year: 2000, location: "L" }));
|
||||
```
|
||||
|
||||
`validateEntry({ title: "Unknown", author: "A", year: 2000, location: "L" })` should return `false`.
|
||||
|
||||
```js
|
||||
assert.isFalse(validateEntry({ title: "Unknown", author: "A", year: 2000, location: "L" }));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
const decade = Math.floor(book.year / 10) * 10;
|
||||
const decadeKey = `${decade}s`;
|
||||
if (!grouped[decadeKey]) {
|
||||
grouped[decadeKey] = [];
|
||||
}
|
||||
grouped[decadeKey].push(book);
|
||||
}
|
||||
return grouped;
|
||||
}
|
||||
|
||||
const byDecade = groupByDecade(catalog);
|
||||
|
||||
function renderEntry(entry) {
|
||||
const title = entry.title || "Unknown";
|
||||
const author = entry.author || "Unknown";
|
||||
const year = entry.year || "Unknown";
|
||||
const location = entry.location || "Unknown";
|
||||
return `${"-".repeat(25)}
|
||||
Title: ${title}
|
||||
Author: ${author}
|
||||
Year: ${year}
|
||||
Location: ${location}
|
||||
${"-".repeat(25)}`;
|
||||
}
|
||||
|
||||
console.log(renderEntry(catalog[0]));
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,155 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee885461
|
||||
title: Step 24
|
||||
challengeType: 1
|
||||
dashedName: step-24
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The same pattern applies to the other three fields. Add equivalent checks for `author`, `year`, and `location`.
|
||||
|
||||
Note that `isValid` accumulates across all four checks: if any field fails, it becomes `false` and stays `false` even if the remaining checks pass. This means the function always inspects every field rather than stopping at the first problem.
|
||||
|
||||
# --hints--
|
||||
|
||||
`validateEntry(catalog[0])` should return `true`.
|
||||
|
||||
```js
|
||||
assert.isTrue(validateEntry(catalog[0]));
|
||||
```
|
||||
|
||||
`validateEntry(catalog[16])` should return `false`.
|
||||
|
||||
```js
|
||||
assert.isFalse(validateEntry(catalog[16]));
|
||||
```
|
||||
|
||||
`validateEntry(catalog[17])` should return `false`.
|
||||
|
||||
```js
|
||||
assert.isFalse(validateEntry(catalog[17]));
|
||||
```
|
||||
|
||||
`validateEntry(catalog[18])` should return `false` because the location is `"Unknown"`.
|
||||
|
||||
```js
|
||||
assert.isFalse(validateEntry(catalog[18]));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
const decade = Math.floor(book.year / 10) * 10;
|
||||
const decadeKey = `${decade}s`;
|
||||
if (!grouped[decadeKey]) {
|
||||
grouped[decadeKey] = [];
|
||||
}
|
||||
grouped[decadeKey].push(book);
|
||||
}
|
||||
return grouped;
|
||||
}
|
||||
|
||||
const byDecade = groupByDecade(catalog);
|
||||
|
||||
function renderEntry(entry) {
|
||||
const title = entry.title || "Unknown";
|
||||
const author = entry.author || "Unknown";
|
||||
const year = entry.year || "Unknown";
|
||||
const location = entry.location || "Unknown";
|
||||
return `${"-".repeat(25)}
|
||||
Title: ${title}
|
||||
Author: ${author}
|
||||
Year: ${year}
|
||||
Location: ${location}
|
||||
${"-".repeat(25)}`;
|
||||
}
|
||||
|
||||
console.log(renderEntry(catalog[0]));
|
||||
|
||||
function validateEntry(entry) {
|
||||
let isValid = true;
|
||||
if (!("title" in entry) || !entry.title || entry.title === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
return isValid;
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,153 @@
|
||||
---
|
||||
id: 699d20623e57e8ffee885462
|
||||
title: Step 25
|
||||
challengeType: 1
|
||||
dashedName: step-25
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Let's see the validator catch a real problem. `catalog[0]` is "From a Buick 8" by Stephen King, which has all four fields populated. `catalog[16]` is "Unknown Title", which has a blank author field.
|
||||
|
||||
Log `validateEntry(catalog[0])` and `validateEntry(catalog[16])`. The first should return `true`; the second should return `false`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should log `validateEntry(catalog[0])` to the console.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*validateEntry\s*\(\s*catalog\s*\[\s*0\s*\]\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
You should log `validateEntry(catalog[16])` to the console.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*validateEntry\s*\(\s*catalog\s*\[\s*16\s*\]\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
const decade = Math.floor(book.year / 10) * 10;
|
||||
const decadeKey = `${decade}s`;
|
||||
if (!grouped[decadeKey]) {
|
||||
grouped[decadeKey] = [];
|
||||
}
|
||||
grouped[decadeKey].push(book);
|
||||
}
|
||||
return grouped;
|
||||
}
|
||||
|
||||
const byDecade = groupByDecade(catalog);
|
||||
|
||||
function renderEntry(entry) {
|
||||
const title = entry.title || "Unknown";
|
||||
const author = entry.author || "Unknown";
|
||||
const year = entry.year || "Unknown";
|
||||
const location = entry.location || "Unknown";
|
||||
return `${"-".repeat(25)}
|
||||
Title: ${title}
|
||||
Author: ${author}
|
||||
Year: ${year}
|
||||
Location: ${location}
|
||||
${"-".repeat(25)}`;
|
||||
}
|
||||
|
||||
console.log(renderEntry(catalog[0]));
|
||||
|
||||
function validateEntry(entry) {
|
||||
let isValid = true;
|
||||
if (!("title" in entry) || !entry.title || entry.title === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("author" in entry) || !entry.author || entry.author === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("year" in entry) || !entry.year || entry.year === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("location" in entry) || !entry.location || entry.location === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,175 @@
|
||||
---
|
||||
id: 69a65722a69bc6f8d87a9952
|
||||
title: Step 26
|
||||
challengeType: 1
|
||||
dashedName: step-26
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
JSON (JavaScript Object Notation) is a standard format for sharing data between systems. `JSON.stringify` converts a JavaScript value into a JSON-formatted string.
|
||||
|
||||
Start by removing the two `validateEntry` console.log calls, as they were only needed for testing. Then create a function named `exportToJSON` that takes `catalog` as a parameter. Return `JSON.stringify(catalog, null, 2)`. The first argument is the data to convert. The second argument (`null`) means no custom filtering. The third (`2`) adds two-space indentation, making the output readable.
|
||||
|
||||
Below the function, log `exportToJSON(catalog.slice(0, 2))` to preview the JSON output for the first two entries.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should remove the `validateEntry` console.log calls.
|
||||
|
||||
```js
|
||||
assert.notMatch(__helpers.removeJSComments(code), /console\.log\s*\(\s*validateEntry/);
|
||||
```
|
||||
|
||||
`exportToJSON` should return a string.
|
||||
|
||||
```js
|
||||
assert.isString(exportToJSON(catalog));
|
||||
```
|
||||
|
||||
`JSON.parse(exportToJSON(catalog))` should produce a valid array.
|
||||
|
||||
```js
|
||||
assert.isArray(JSON.parse(exportToJSON(catalog)));
|
||||
```
|
||||
|
||||
You should use `JSON.stringify` with `null` and `2` as the second and third arguments.
|
||||
|
||||
```js
|
||||
assert.match(exportToJSON.toString(), /JSON\.stringify\s*\(\s*catalog\s*,\s*null\s*,\s*2\s*\)/);
|
||||
```
|
||||
|
||||
You should log `exportToJSON(catalog.slice(0, 2))` to the console.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*exportToJSON\s*\(/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
const decade = Math.floor(book.year / 10) * 10;
|
||||
const decadeKey = `${decade}s`;
|
||||
if (!grouped[decadeKey]) {
|
||||
grouped[decadeKey] = [];
|
||||
}
|
||||
grouped[decadeKey].push(book);
|
||||
}
|
||||
return grouped;
|
||||
}
|
||||
|
||||
const byDecade = groupByDecade(catalog);
|
||||
|
||||
function renderEntry(entry) {
|
||||
const title = entry.title || "Unknown";
|
||||
const author = entry.author || "Unknown";
|
||||
const year = entry.year || "Unknown";
|
||||
const location = entry.location || "Unknown";
|
||||
return `${"-".repeat(25)}
|
||||
Title: ${title}
|
||||
Author: ${author}
|
||||
Year: ${year}
|
||||
Location: ${location}
|
||||
${"-".repeat(25)}`;
|
||||
}
|
||||
|
||||
console.log(renderEntry(catalog[0]));
|
||||
|
||||
function validateEntry(entry) {
|
||||
let isValid = true;
|
||||
if (!("title" in entry) || !entry.title || entry.title === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("author" in entry) || !entry.author || entry.author === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("year" in entry) || !entry.year || entry.year === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("location" in entry) || !entry.location || entry.location === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
console.log(validateEntry(catalog[0]));
|
||||
console.log(validateEntry(catalog[16]));
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,172 @@
|
||||
---
|
||||
id: 69a65722a69bc6f8d87a9953
|
||||
title: Step 27
|
||||
challengeType: 1
|
||||
dashedName: step-27
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
CSV (Comma-Separated Values) is a format used for spreadsheets and data import. Each line represents one entry with fields separated by commas.
|
||||
|
||||
Start by removing the `console.log(exportToJSON(...))` call, as it was only needed for testing. Then create a function named `exportToCSV` that takes `catalog` as a parameter. Inside, declare `const header = "Title,Author,Year,Location"` and `const rows = []`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should remove the `exportToJSON` console.log call.
|
||||
|
||||
```js
|
||||
assert.notMatch(__helpers.removeJSComments(code), /console\.log\s*\(\s*exportToJSON/);
|
||||
```
|
||||
|
||||
You should have a function named `exportToCSV` with a `catalog` parameter.
|
||||
|
||||
```js
|
||||
const regex = __helpers.functionRegex('exportToCSV', ['catalog']);
|
||||
assert.match(__helpers.removeJSComments(code), regex);
|
||||
```
|
||||
|
||||
You should declare a variable named `header` with value `"Title,Author,Year,Location"`.
|
||||
|
||||
```js
|
||||
assert.match(exportToCSV.toString(), /Title,Author,Year,Location/);
|
||||
```
|
||||
|
||||
You should declare a variable named `rows` set to an empty array.
|
||||
|
||||
```js
|
||||
assert.match(exportToCSV.toString(), /(const|let|var)\s+rows\s*=\s*\[\s*\]/);
|
||||
```
|
||||
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
const decade = Math.floor(book.year / 10) * 10;
|
||||
const decadeKey = `${decade}s`;
|
||||
if (!grouped[decadeKey]) {
|
||||
grouped[decadeKey] = [];
|
||||
}
|
||||
grouped[decadeKey].push(book);
|
||||
}
|
||||
return grouped;
|
||||
}
|
||||
|
||||
const byDecade = groupByDecade(catalog);
|
||||
|
||||
function renderEntry(entry) {
|
||||
const title = entry.title || "Unknown";
|
||||
const author = entry.author || "Unknown";
|
||||
const year = entry.year || "Unknown";
|
||||
const location = entry.location || "Unknown";
|
||||
return `${"-".repeat(25)}
|
||||
Title: ${title}
|
||||
Author: ${author}
|
||||
Year: ${year}
|
||||
Location: ${location}
|
||||
${"-".repeat(25)}`;
|
||||
}
|
||||
|
||||
console.log(renderEntry(catalog[0]));
|
||||
|
||||
function validateEntry(entry) {
|
||||
let isValid = true;
|
||||
if (!("title" in entry) || !entry.title || entry.title === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("author" in entry) || !entry.author || entry.author === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("year" in entry) || !entry.year || entry.year === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("location" in entry) || !entry.location || entry.location === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function exportToJSON(catalog) {
|
||||
return JSON.stringify(catalog, null, 2);
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
console.log(exportToJSON(catalog.slice(0, 2)));
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,364 @@
|
||||
---
|
||||
id: 69a65722a69bc6f8d87a9954
|
||||
title: Step 30
|
||||
challengeType: 1
|
||||
dashedName: step-30
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Let's finish with a summary. Log `catalog.length` and `Object.keys(byDecade).length` to see total books and decade groups.
|
||||
|
||||
Declare `let oldestYear = Infinity` and `let newestYear = 0`. Use a `for` loop over `catalog`: for each entry where the year is not `"Unknown"`, update `oldestYear` if the year is smaller and `newestYear` if it is larger. Log both values.
|
||||
|
||||
You have completed the Heritage Library Catalog workshop!
|
||||
|
||||
# --hints--
|
||||
|
||||
You should log `catalog.length` to the console.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*catalog\.length\s*\)/);
|
||||
```
|
||||
|
||||
You should log `Object.keys(byDecade).length` to the console.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*Object\.keys\s*\(\s*byDecade\s*\)\.length\s*\)/);
|
||||
```
|
||||
|
||||
You should use a `for` loop over `catalog` to find the oldest and newest years.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /for\s*\(\s*(var|let)\s+(\w+)\s*=\s*0\s*;\s*\2\s*<\s*catalog\s*\.\s*length\s*;\s*\2\+\+\s*\)\s*\{/);
|
||||
```
|
||||
|
||||
The oldest year in the catalog should be `850`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(oldestYear, 850);
|
||||
```
|
||||
|
||||
The newest year in the catalog should be `2011`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(newestYear, 2011);
|
||||
```
|
||||
|
||||
You should log `oldestYear` to the console.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*oldestYear\s*\)/);
|
||||
```
|
||||
|
||||
You should log `newestYear` to the console.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*newestYear\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
const decade = Math.floor(book.year / 10) * 10;
|
||||
const decadeKey = `${decade}s`;
|
||||
if (!grouped[decadeKey]) {
|
||||
grouped[decadeKey] = [];
|
||||
}
|
||||
grouped[decadeKey].push(book);
|
||||
}
|
||||
return grouped;
|
||||
}
|
||||
|
||||
const byDecade = groupByDecade(catalog);
|
||||
|
||||
function renderEntry(entry) {
|
||||
const title = entry.title || "Unknown";
|
||||
const author = entry.author || "Unknown";
|
||||
const year = entry.year || "Unknown";
|
||||
const location = entry.location || "Unknown";
|
||||
return `${"-".repeat(25)}
|
||||
Title: ${title}
|
||||
Author: ${author}
|
||||
Year: ${year}
|
||||
Location: ${location}
|
||||
${"-".repeat(25)}`;
|
||||
}
|
||||
|
||||
console.log(renderEntry(catalog[0]));
|
||||
|
||||
function validateEntry(entry) {
|
||||
let isValid = true;
|
||||
if (!("title" in entry) || !entry.title || entry.title === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("author" in entry) || !entry.author || entry.author === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("year" in entry) || !entry.year || entry.year === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("location" in entry) || !entry.location || entry.location === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function exportToJSON(catalog) {
|
||||
return JSON.stringify(catalog, null, 2);
|
||||
}
|
||||
|
||||
function exportToCSV(catalog) {
|
||||
const header = "Title,Author,Year,Location";
|
||||
const rows = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const entry = catalog[i];
|
||||
rows.push(`"${entry.title}","${entry.author}",${entry.year},"${entry.location}"`);
|
||||
}
|
||||
let csv = header;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
csv = csv + "\n" + rows[i];
|
||||
}
|
||||
return csv;
|
||||
}
|
||||
|
||||
console.log(exportToCSV(catalog));
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
const decade = Math.floor(book.year / 10) * 10;
|
||||
const decadeKey = `${decade}s`;
|
||||
if (!grouped[decadeKey]) {
|
||||
grouped[decadeKey] = [];
|
||||
}
|
||||
grouped[decadeKey].push(book);
|
||||
}
|
||||
return grouped;
|
||||
}
|
||||
|
||||
const byDecade = groupByDecade(catalog);
|
||||
|
||||
function renderEntry(entry) {
|
||||
const title = entry.title || "Unknown";
|
||||
const author = entry.author || "Unknown";
|
||||
const year = entry.year || "Unknown";
|
||||
const location = entry.location || "Unknown";
|
||||
return `${"-".repeat(25)}
|
||||
Title: ${title}
|
||||
Author: ${author}
|
||||
Year: ${year}
|
||||
Location: ${location}
|
||||
${"-".repeat(25)}`;
|
||||
}
|
||||
|
||||
console.log(renderEntry(catalog[0]));
|
||||
|
||||
function validateEntry(entry) {
|
||||
let isValid = true;
|
||||
if (!("title" in entry) || !entry.title || entry.title === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("author" in entry) || !entry.author || entry.author === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("year" in entry) || !entry.year || entry.year === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("location" in entry) || !entry.location || entry.location === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function exportToJSON(catalog) {
|
||||
return JSON.stringify(catalog, null, 2);
|
||||
}
|
||||
|
||||
function exportToCSV(catalog) {
|
||||
const header = "Title,Author,Year,Location";
|
||||
const rows = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const entry = catalog[i];
|
||||
rows.push(`"${entry.title}","${entry.author}",${entry.year},"${entry.location}"`);
|
||||
}
|
||||
let csv = header;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
csv = csv + "\n" + rows[i];
|
||||
}
|
||||
return csv;
|
||||
}
|
||||
|
||||
console.log(exportToCSV(catalog));
|
||||
|
||||
console.log(catalog.length);
|
||||
console.log(Object.keys(byDecade).length);
|
||||
let oldestYear = Infinity;
|
||||
let newestYear = 0;
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const year = catalog[i].year;
|
||||
if (year !== "Unknown") {
|
||||
if (year < oldestYear) {
|
||||
oldestYear = year;
|
||||
}
|
||||
if (year > newestYear) {
|
||||
newestYear = year;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(oldestYear);
|
||||
console.log(newestYear);
|
||||
```
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
id: 69a9de737382f5b679d7e169
|
||||
title: Step 13
|
||||
challengeType: 1
|
||||
dashedName: step-13
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
To find matching authors, check each catalog entry one by one.
|
||||
|
||||
Add an empty `for` loop inside `findByAuthor` to iterate over `catalog`. You will add the loop body in the next step.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use a `for` loop to iterate over `catalog`.
|
||||
|
||||
```js
|
||||
assert.match(findByAuthor.toString(), /for\s*\(\s*(var|let)\s+(\w+)\s*=\s*0\s*;\s*\2\s*<\s*catalog\s*\.\s*length\s*;\s*\2\+\+\s*\)\s*\{/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
id: 69a9de7ceb9b18f4fc988306
|
||||
title: Step 14
|
||||
challengeType: 1
|
||||
dashedName: step-14
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Inside the loop, check whether the current entry's author (converted to lowercase) includes `searchTerm`. The `.includes()` method returns `true` if a string contains another string as a substring. If the condition is true, push the entry into `results`. After the loop, return `results`.
|
||||
|
||||
# --hints--
|
||||
|
||||
`findByAuthor(catalog, "king").length` should be `6`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(findByAuthor(catalog, "king").length, 6);
|
||||
```
|
||||
|
||||
`findByAuthor(catalog, "asimov").length` should be `2`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(findByAuthor(catalog, "asimov").length, 2);
|
||||
```
|
||||
|
||||
`findByAuthor(catalog, "xyz").length` should be `0`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(findByAuthor(catalog, "xyz").length, 0);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
--fcc-editable-region--
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
id: 69a9de82b435a1310aff57c2
|
||||
title: Step 18
|
||||
challengeType: 1
|
||||
dashedName: step-18
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Some books have `"Unknown"` as their year. We will handle that edge case first.
|
||||
|
||||
Inside the loop, check if `book.year` is equal to the string `"Unknown"`. Then, check if `grouped["Unknown"]` doesn't exist yet and initialize it as an empty array. Push `book` into `grouped["Unknown"]`.
|
||||
|
||||
Finally, use `continue` to skip the rest of the loop body. The `continue` statement jumps directly to the next iteration.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should check if `book.year` is equal to `"Unknown"`.
|
||||
|
||||
```js
|
||||
assert.match(groupByDecade.toString(), /book\.year\s*===?\s*"Unknown"/);
|
||||
```
|
||||
|
||||
You should check if `grouped["Unknown"]` doesn't exist yet and initialize it as an empty array.
|
||||
|
||||
```js
|
||||
assert.match(groupByDecade.toString(), /!\s*grouped\s*\[\s*"Unknown"\s*\]/);
|
||||
assert.match(groupByDecade.toString(), /grouped\s*\[\s*"Unknown"\s*\]\s*=\s*\[\s*\]/);
|
||||
```
|
||||
|
||||
You should push `book` into `grouped["Unknown"]`.
|
||||
|
||||
```js
|
||||
assert.match(groupByDecade.toString(), /grouped\s*\[\s*"Unknown"\s*\]\s*\.push\s*\(\s*book\s*\)/);
|
||||
```
|
||||
|
||||
You should use `continue` to skip to the next iteration after handling an unknown year.
|
||||
|
||||
```js
|
||||
assert.match(groupByDecade.toString(), /\bcontinue\b/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,168 @@
|
||||
---
|
||||
id: 69a9de8abda299f753b1afe4
|
||||
title: Step 28
|
||||
challengeType: 1
|
||||
dashedName: step-28
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Add a `for` loop over `catalog`. Inside the loop, declare `const entry = catalog[i]`. Build a row string with the four fields separated by commas and push it into `rows`.
|
||||
|
||||
String fields need literal double-quote characters in the output (e.g. `"King, Stephen"`), so that commas inside values don't break the CSV format. Since `year` is a number, it doesn't need them.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use a `for` loop to iterate over `catalog`.
|
||||
|
||||
```js
|
||||
assert.match(exportToCSV.toString(), /for\s*\(\s*(var|let)\s+(\w+)\s*=\s*0\s*;\s*\2\s*<\s*catalog\s*\.\s*length\s*;\s*\2\+\+\s*\)\s*\{/);
|
||||
```
|
||||
|
||||
Each row should have the entry's fields separated by commas.
|
||||
|
||||
```js
|
||||
assert.match(exportToCSV.toString(), /rows\.push/);
|
||||
```
|
||||
|
||||
String fields should be wrapped in double quotes.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /"\$\{entry\./);
|
||||
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
const decade = Math.floor(book.year / 10) * 10;
|
||||
const decadeKey = `${decade}s`;
|
||||
if (!grouped[decadeKey]) {
|
||||
grouped[decadeKey] = [];
|
||||
}
|
||||
grouped[decadeKey].push(book);
|
||||
}
|
||||
return grouped;
|
||||
}
|
||||
|
||||
const byDecade = groupByDecade(catalog);
|
||||
|
||||
function renderEntry(entry) {
|
||||
const title = entry.title || "Unknown";
|
||||
const author = entry.author || "Unknown";
|
||||
const year = entry.year || "Unknown";
|
||||
const location = entry.location || "Unknown";
|
||||
return `${"-".repeat(25)}
|
||||
Title: ${title}
|
||||
Author: ${author}
|
||||
Year: ${year}
|
||||
Location: ${location}
|
||||
${"-".repeat(25)}`;
|
||||
}
|
||||
|
||||
console.log(renderEntry(catalog[0]));
|
||||
|
||||
function validateEntry(entry) {
|
||||
let isValid = true;
|
||||
if (!("title" in entry) || !entry.title || entry.title === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("author" in entry) || !entry.author || entry.author === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("year" in entry) || !entry.year || entry.year === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("location" in entry) || !entry.location || entry.location === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function exportToJSON(catalog) {
|
||||
return JSON.stringify(catalog, null, 2);
|
||||
}
|
||||
|
||||
function exportToCSV(catalog) {
|
||||
const header = "Title,Author,Year,Location";
|
||||
const rows = [];
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,178 @@
|
||||
---
|
||||
id: 69a9de93aa714a3b0105b051
|
||||
title: Step 29
|
||||
challengeType: 1
|
||||
dashedName: step-29
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now combine all rows into one CSV string you can export or share.
|
||||
|
||||
After the first loop, declare `let csv = header`. Add another `for` loop to append each row to `csv` with a newline: `csv = csv + "\n" + rows[i]`. Return `csv`. Then, below the function, log `exportToCSV(catalog)` to preview the formatted output.
|
||||
|
||||
# --hints--
|
||||
|
||||
`exportToCSV` should return a string.
|
||||
|
||||
```js
|
||||
assert.isString(exportToCSV(catalog));
|
||||
```
|
||||
|
||||
The first line of the returned string should be `"Title,Author,Year,Location"`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(exportToCSV(catalog).split("\n")[0], "Title,Author,Year,Location");
|
||||
```
|
||||
|
||||
The output should contain `"King, Stephen"` (quoted to handle the comma).
|
||||
|
||||
```js
|
||||
assert.include(exportToCSV(catalog), '"King, Stephen"');
|
||||
```
|
||||
|
||||
You should log `exportToCSV(catalog)` to the console.
|
||||
|
||||
```js
|
||||
assert.match(__helpers.removeJSComments(code), /console\.log\s*\(\s*exportToCSV\s*\(\s*catalog\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
const decade = Math.floor(book.year / 10) * 10;
|
||||
const decadeKey = `${decade}s`;
|
||||
if (!grouped[decadeKey]) {
|
||||
grouped[decadeKey] = [];
|
||||
}
|
||||
grouped[decadeKey].push(book);
|
||||
}
|
||||
return grouped;
|
||||
}
|
||||
|
||||
const byDecade = groupByDecade(catalog);
|
||||
|
||||
function renderEntry(entry) {
|
||||
const title = entry.title || "Unknown";
|
||||
const author = entry.author || "Unknown";
|
||||
const year = entry.year || "Unknown";
|
||||
const location = entry.location || "Unknown";
|
||||
return `${"-".repeat(25)}
|
||||
Title: ${title}
|
||||
Author: ${author}
|
||||
Year: ${year}
|
||||
Location: ${location}
|
||||
${"-".repeat(25)}`;
|
||||
}
|
||||
|
||||
console.log(renderEntry(catalog[0]));
|
||||
|
||||
function validateEntry(entry) {
|
||||
let isValid = true;
|
||||
if (!("title" in entry) || !entry.title || entry.title === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("author" in entry) || !entry.author || entry.author === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("year" in entry) || !entry.year || entry.year === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
if (!("location" in entry) || !entry.location || entry.location === "Unknown") {
|
||||
isValid = false;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function exportToJSON(catalog) {
|
||||
return JSON.stringify(catalog, null, 2);
|
||||
}
|
||||
|
||||
function exportToCSV(catalog) {
|
||||
const header = "Title,Author,Year,Location";
|
||||
const rows = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const entry = catalog[i];
|
||||
rows.push(`"${entry.title}","${entry.author}",${entry.year},"${entry.location}"`);
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
id: 69aa11c2584523e6e991330c
|
||||
title: Step 6
|
||||
challengeType: 1
|
||||
dashedName: step-6
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `.trim()` method removes whitespace from both ends of a string. For example, `" hello ".trim()` returns `"hello"`.
|
||||
|
||||
Inside the loop body, push `parts[i].trim()` into `trimmedParts`. Then update the existing `return` statement to return `trimmedParts` instead of `parts`.
|
||||
|
||||
After this step, the logged output should show clean values without extra spaces.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should push `parts[i].trim()` into `trimmedParts` inside the loop.
|
||||
|
||||
```js
|
||||
assert.match(parseCard.toString(), /trimmedParts\s*\.\s*push\s*\(\s*parts\s*\[\s*\w+\s*\]\s*\.\s*trim\s*\(\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
`parseCard` should return `trimmedParts`.
|
||||
|
||||
```js
|
||||
const explorer = await __helpers.Explorer(code);
|
||||
const { parseCard } = explorer.allFunctions;
|
||||
assert.isTrue(parseCard.hasReturn("trimmedParts"));
|
||||
```
|
||||
|
||||
`parseCard` should return an array where each element has no leading or trailing whitespace.
|
||||
|
||||
```js
|
||||
assert.isArray(parseCard(" A | B | C | D "));
|
||||
```
|
||||
|
||||
When called with `" A | B | C | D "`, the first element of the returned array should be `"A"`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(parseCard(" A | B | C | D ")[0], "A");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
--fcc-editable-region--
|
||||
|
||||
}
|
||||
return parts;
|
||||
--fcc-editable-region--
|
||||
}
|
||||
|
||||
const cardResult = parseCard(rawCatalogCards[2]);
|
||||
console.log(cardResult);
|
||||
```
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
id: 69b8f3a2c1d4e5f6a7b8c9d0
|
||||
title: Step 11
|
||||
challengeType: 1
|
||||
dashedName: step-11
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`parseCatalog` currently returns an empty array. To fill it, add a `for` loop inside the function that iterates over `rawCards`. Inside the loop, call `parseCard` on each element and push the result into `catalog`.
|
||||
|
||||
# --hints--
|
||||
|
||||
`parseCatalog(rawCatalogCards).length` should be `19`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(parseCatalog(rawCatalogCards).length, 19);
|
||||
```
|
||||
|
||||
`parseCatalog` should use a `for` loop to iterate over `rawCards`.
|
||||
|
||||
```js
|
||||
assert.match(parseCatalog.toString(), /for\s*\(\s*(var|let)\s+(\w+)\s*=\s*0\s*;\s*\2\s*<\s*rawCards\s*\.\s*length\s*;\s*\2\+\+\s*\)\s*\{/);
|
||||
```
|
||||
|
||||
You should call `parseCard` inside the loop.
|
||||
|
||||
```js
|
||||
assert.match(parseCatalog.toString(), /parseCard\s*\(/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
console.log(catalog.length);
|
||||
```
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
id: 69c2a3b4d5e6f7a8b9c0d1e2
|
||||
title: Step 16
|
||||
challengeType: 1
|
||||
dashedName: step-16
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `kingBooks` test code confirmed that `findByAuthor` works correctly. Before moving on to `groupByDecade`, clean up the console output by removing it.
|
||||
|
||||
Remove the `const kingBooks = ...` line, `console.log(kingBooks.length)`, and the `for` loop that displays the book titles and years.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `kingBooks` variable, the `console.log(kingBooks.length)` statement, and the `for` loop should all be removed.
|
||||
|
||||
```js
|
||||
assert.notMatch(__helpers.removeJSComments(code), /\bkingBooks\b/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const kingBooks = findByAuthor(catalog, "king");
|
||||
console.log(kingBooks.length);
|
||||
for (let i = 0; i < kingBooks.length; i++) {
|
||||
console.log(`${kingBooks[i].title} (${kingBooks[i].year})`);
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,149 @@
|
||||
---
|
||||
id: 69d5e6f7a8b9c0d1e2f3a4b5
|
||||
title: Step 22
|
||||
challengeType: 1
|
||||
dashedName: step-22
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now fill in `renderEntry`. Declare a fallback constant for each field: `const title = entry.title || "Unknown"`, and apply the same pattern for `author`, `year`, and `location`.
|
||||
|
||||
Return a multi-line template literal that formats the entry as a library card. Use `"-".repeat(25)` for the top and bottom border, and include labeled lines for `Title`, `Author`, `Year`, and `Location`. For example, a labeled line for the title would look like `Title: ${title}`.
|
||||
|
||||
# --hints--
|
||||
|
||||
`renderEntry` should return a string.
|
||||
|
||||
```js
|
||||
assert.isString(renderEntry(catalog[0]));
|
||||
```
|
||||
|
||||
When called with `catalog[0]`, the returned string should contain `"From a Buick 8"`.
|
||||
|
||||
```js
|
||||
assert.include(renderEntry(catalog[0]), "From a Buick 8");
|
||||
```
|
||||
|
||||
When called with `catalog[0]`, the returned string should contain `"King, Stephen"`.
|
||||
|
||||
```js
|
||||
assert.include(renderEntry(catalog[0]), "King, Stephen");
|
||||
```
|
||||
|
||||
When called with `catalog[0]`, the returned string should contain the year `2002`.
|
||||
|
||||
```js
|
||||
assert.include(renderEntry(catalog[0]), "2002");
|
||||
```
|
||||
|
||||
When called with `catalog[0]`, the returned string should contain `"Shelf K7"`.
|
||||
|
||||
```js
|
||||
assert.include(renderEntry(catalog[0]), "Shelf K7");
|
||||
```
|
||||
|
||||
The returned string should include a top and bottom border using `"-".repeat(25)`.
|
||||
|
||||
```js
|
||||
assert.isAbove(renderEntry(catalog[0]).split("-".repeat(25)).length, 2);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```js
|
||||
const rawCatalogCards = [
|
||||
"From a Buick 8 | King, Stephen | 2002 | Shelf K7",
|
||||
"The Shining | King, Stephen | 1977 | Shelf K1",
|
||||
"The Stand | King, Stephen | 1978 | Shelf K2",
|
||||
"It | King, Stephen | 1986 | Shelf K3",
|
||||
"Misery | King, Stephen | 1987 | Shelf K4",
|
||||
"Do Androids Dream of Electric Sheep? | Dick, Philip K. | 1968 | Shelf D5",
|
||||
"I, Robot | Asimov, Isaac | 1950 | Shelf A8",
|
||||
"Foundation | Asimov, Isaac | 1951 | Shelf A9",
|
||||
"Dune | Herbert, Frank | 1965 | Shelf H3",
|
||||
"Neuromancer | Gibson, William | 1984 | Shelf G8",
|
||||
"Snow Crash | Stephenson, Neal | 1992 | Shelf S6",
|
||||
"The Martian | Weir, Andy | 2011 | Shelf W5",
|
||||
"Ender's Game | Card, Orson Scott | 1985 | Shelf C2",
|
||||
"The Hitchhiker's Guide to the Galaxy | Adams, Douglas | 1979 | Shelf A1",
|
||||
"Ready Player One | Cline, Ernest | 2011 | Shelf C7",
|
||||
"The Dark Tower: The Gunslinger | King, Stephen | 1982 | Shelf K5",
|
||||
// edge cases: missing data
|
||||
"Unknown Title | | 1975 | Shelf X1",
|
||||
"Mysterious Manuscript | Unknown Author | | Shelf Z9",
|
||||
"Ancient Scroll | Anonymous | 850 | ",
|
||||
];
|
||||
|
||||
function parseCard(rawString) {
|
||||
const parts = rawString.split("|");
|
||||
const trimmedParts = [];
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
trimmedParts.push(parts[i].trim());
|
||||
}
|
||||
const title = trimmedParts[0];
|
||||
const author = trimmedParts[1];
|
||||
const year = trimmedParts[2];
|
||||
const location = trimmedParts[3];
|
||||
return {
|
||||
title: title || "Unknown",
|
||||
author: author || "Unknown",
|
||||
year: year ? parseInt(year) : "Unknown",
|
||||
location: location || "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
function parseCatalog(rawCards) {
|
||||
const catalog = [];
|
||||
for (let i = 0; i < rawCards.length; i++) {
|
||||
catalog.push(parseCard(rawCards[i]));
|
||||
}
|
||||
return catalog;
|
||||
}
|
||||
|
||||
const catalog = parseCatalog(rawCatalogCards);
|
||||
|
||||
function findByAuthor(catalog, author) {
|
||||
const searchTerm = author.toLowerCase();
|
||||
const results = [];
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
if (catalog[i].author.toLowerCase().includes(searchTerm)) {
|
||||
results.push(catalog[i]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function groupByDecade(catalog) {
|
||||
const grouped = {};
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const book = catalog[i];
|
||||
if (book.year === "Unknown") {
|
||||
if (!grouped["Unknown"]) {
|
||||
grouped["Unknown"] = [];
|
||||
}
|
||||
grouped["Unknown"].push(book);
|
||||
continue;
|
||||
}
|
||||
const decade = Math.floor(book.year / 10) * 10;
|
||||
const decadeKey = `${decade}s`;
|
||||
if (!grouped[decadeKey]) {
|
||||
grouped[decadeKey] = [];
|
||||
}
|
||||
grouped[decadeKey].push(book);
|
||||
}
|
||||
return grouped;
|
||||
}
|
||||
|
||||
const byDecade = groupByDecade(catalog);
|
||||
|
||||
function renderEntry(entry) {
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
}
|
||||
|
||||
console.log(renderEntry(catalog[0]));
|
||||
```
|
||||
@@ -109,5 +109,6 @@
|
||||
"fho5t5": "When you open a new tab at the end",
|
||||
"00kcrm": "yields true",
|
||||
"sxpg2a": "Your mailbox, drive, and other work sites",
|
||||
"n4f332": "TODO: use a different type of loop"
|
||||
"n4f332": "TODO: use a different type of loop",
|
||||
"k7m2vx": "edge cases: missing data"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"isUpcomingChange": false,
|
||||
"dashedName": "workshop-heritage-library-catalog",
|
||||
"helpCategory": "JavaScript",
|
||||
"blockLayout": "challenge-grid",
|
||||
"challengeOrder": [
|
||||
{ "id": "699d1f8e51fcdc8230f779fb", "title": "Step 1" },
|
||||
{ "id": "699d20623e57e8ffee885450", "title": "Step 2" },
|
||||
{ "id": "699d20623e57e8ffee885451", "title": "Step 3" },
|
||||
{ "id": "699d20623e57e8ffee885454", "title": "Step 4" },
|
||||
{ "id": "699d20623e57e8ffee885455", "title": "Step 5" },
|
||||
{ "id": "69aa11c2584523e6e991330c", "title": "Step 6" },
|
||||
{ "id": "699d20623e57e8ffee885456", "title": "Step 7" },
|
||||
{ "id": "699d20623e57e8ffee885457", "title": "Step 8" },
|
||||
{ "id": "699d20623e57e8ffee885458", "title": "Step 9" },
|
||||
{ "id": "699d20623e57e8ffee885459", "title": "Step 10" },
|
||||
{ "id": "69b8f3a2c1d4e5f6a7b8c9d0", "title": "Step 11" },
|
||||
{ "id": "699d20623e57e8ffee88545a", "title": "Step 12" },
|
||||
{ "id": "69a9de737382f5b679d7e169", "title": "Step 13" },
|
||||
{ "id": "69a9de7ceb9b18f4fc988306", "title": "Step 14" },
|
||||
{ "id": "699d20623e57e8ffee88545b", "title": "Step 15" },
|
||||
{ "id": "69c2a3b4d5e6f7a8b9c0d1e2", "title": "Step 16" },
|
||||
{ "id": "699d20623e57e8ffee88545c", "title": "Step 17" },
|
||||
{ "id": "69a9de82b435a1310aff57c2", "title": "Step 18" },
|
||||
{ "id": "699d20623e57e8ffee88545d", "title": "Step 19" },
|
||||
{ "id": "699d20623e57e8ffee88545e", "title": "Step 20" },
|
||||
{ "id": "699d20623e57e8ffee88545f", "title": "Step 21" },
|
||||
{ "id": "69d5e6f7a8b9c0d1e2f3a4b5", "title": "Step 22" },
|
||||
{ "id": "699d20623e57e8ffee885460", "title": "Step 23" },
|
||||
{ "id": "699d20623e57e8ffee885461", "title": "Step 24" },
|
||||
{ "id": "699d20623e57e8ffee885462", "title": "Step 25" },
|
||||
{ "id": "69a65722a69bc6f8d87a9952", "title": "Step 26" },
|
||||
{ "id": "69a65722a69bc6f8d87a9953", "title": "Step 27" },
|
||||
{ "id": "69a9de8abda299f753b1afe4", "title": "Step 28" },
|
||||
{ "id": "69a9de93aa714a3b0105b051", "title": "Step 29" },
|
||||
{ "id": "69a65722a69bc6f8d87a9954", "title": "Step 30" }
|
||||
],
|
||||
"blockLabel": "workshop",
|
||||
"usesMultifileEditor": true,
|
||||
"hasEditableBoundaries": true
|
||||
}
|
||||
@@ -99,6 +99,7 @@
|
||||
"lecture-working-with-loops",
|
||||
"workshop-sentence-analyzer",
|
||||
"workshop-space-mission-roster",
|
||||
"workshop-heritage-library-catalog",
|
||||
"lab-longest-word-in-a-string",
|
||||
"lab-factorial-calculator",
|
||||
"lab-mutations",
|
||||
|
||||
Reference in New Issue
Block a user