@icarusmx/creta 1.5.3 → 1.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/creta.js +14 -4
- package/lib/builders/MenuBuilder.js +23 -4
- package/lib/executors/ExercisesExecutor.js +271 -0
- package/lib/exercises/array-object-manipulation.md +1281 -0
- package/lib/exercises/git-stash-workflow.md +426 -0
- package/lib/exercises/railway-deployment.md +1185 -0
- package/lib/papers/bitcoin/bitcoin.md +92 -0
- package/lib/papers/mapreduce/mapreduce.md +476 -0
- package/lib/papers/spark/spark.md +49 -0
- package/package.json +5 -1
- package/ascii-logo.txt +0 -8
- package/codex-refactor.txt +0 -13
- package/docs/diagrams/README.md +0 -131
- package/docs/diagrams/architecture-overview.mmd +0 -71
- package/docs/diagrams/architecture.svg +0 -1
- package/docs/diagrams/ecosystem-integration.mmd +0 -49
- package/docs/diagrams/evolution-phases.mmd +0 -49
- package/docs/diagrams/output.svg +0 -1
- package/docs/diagrams/phase2-command-help-flow.mmd +0 -51
- package/docs/diagrams/user-journey.mmd +0 -78
- package/ejemplo.sh +0 -3
- package/refactor.txt +0 -581
- package/templates/sveltekit-portfolio/package.json +0 -20
- package/templates/sveltekit-portfolio/src/app.css +0 -51
- package/templates/sveltekit-portfolio/src/app.html +0 -12
- package/templates/sveltekit-portfolio/src/routes/+layout.svelte +0 -108
- package/templates/sveltekit-portfolio/src/routes/+page.svelte +0 -79
- package/templates/sveltekit-portfolio/static/favicon.png +0 -0
- package/templates/sveltekit-portfolio/svelte.config.js +0 -10
- package/templates/sveltekit-portfolio/vite.config.js +0 -10
- package/test/enunciados.test.js +0 -72
- package/test/sintaxis-menu.test.js +0 -45
- package/wea-fome-qlia.sh +0 -92
|
@@ -0,0 +1,1281 @@
|
|
|
1
|
+
# Array & Object Manipulation Warmups
|
|
2
|
+
|
|
3
|
+
<!-- vim: set foldmethod=marker foldlevel=0: -->
|
|
4
|
+
|
|
5
|
+
## 📖 LazyVim Reading Guide {{{
|
|
6
|
+
|
|
7
|
+
**Start with:** `zM` (close all folds) → Navigate with `za` (toggle fold under cursor)
|
|
8
|
+
|
|
9
|
+
This document uses fold markers `{{{` and `}}}` for organized reading.
|
|
10
|
+
|
|
11
|
+
**Navigation Tips:**
|
|
12
|
+
- `zM` - Close all folds (see just section headers)
|
|
13
|
+
- `zR` - Open all folds (see everything)
|
|
14
|
+
- `za` - Toggle current fold
|
|
15
|
+
- `zj` - Jump to next fold
|
|
16
|
+
- `zk` - Jump to previous fold
|
|
17
|
+
|
|
18
|
+
}}}
|
|
19
|
+
|
|
20
|
+
## 🎯 Learning Path {{{
|
|
21
|
+
|
|
22
|
+
This document progresses from **basic object access** to **advanced array transformations**:
|
|
23
|
+
|
|
24
|
+
1. **Level 1: Objects** - Access, modify, destructure
|
|
25
|
+
2. **Level 2: Basic Arrays** - Map, filter, find
|
|
26
|
+
3. **Level 3: Array of Objects** - Query, transform, aggregate
|
|
27
|
+
4. **Level 4: Advanced Patterns** - Reduce, grouping, chaining
|
|
28
|
+
|
|
29
|
+
**How to practice:**
|
|
30
|
+
- Copy exercise to a `.js` file
|
|
31
|
+
- Use `node filename.js` to test
|
|
32
|
+
- Try solving WITHOUT looking at solution first
|
|
33
|
+
- Compare your solution with the provided one
|
|
34
|
+
|
|
35
|
+
}}}
|
|
36
|
+
|
|
37
|
+
## 💡 LazyVim Editing Tips {{{
|
|
38
|
+
|
|
39
|
+
### Quick Object/Array Navigation {{{
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
// Use these motions in LazyVim:
|
|
43
|
+
const user = {
|
|
44
|
+
name: "Ana", // ci{ inside curly braces
|
|
45
|
+
age: 25, // % jump to matching bracket
|
|
46
|
+
city: "CDMX" // va{ select around object (includes {})
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Key Motions:**
|
|
51
|
+
- `%` - Jump between matching brackets `{}`, `[]`, `()`
|
|
52
|
+
- `ci{` - Change inside `{}`
|
|
53
|
+
- `di[` - Delete inside `[]`
|
|
54
|
+
- `va{` - Visual select around `{}` (includes braces)
|
|
55
|
+
- `vi[` - Visual select inside `[]` (excludes brackets)
|
|
56
|
+
- `>>` - Indent line
|
|
57
|
+
- `<<` - Unindent line
|
|
58
|
+
- `=i{` - Auto-format inside `{}`
|
|
59
|
+
|
|
60
|
+
}}}
|
|
61
|
+
|
|
62
|
+
### Array Manipulation Shortcuts {{{
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
// Working with arrays in LazyVim:
|
|
66
|
+
const nums = [1, 2, 3, 4, 5]
|
|
67
|
+
// ↑ ↑
|
|
68
|
+
// ci[ press % to jump
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Multi-line arrays:**
|
|
72
|
+
- `V` - Visual line mode
|
|
73
|
+
- `>` - Indent selected lines
|
|
74
|
+
- `<` - Unindent selected lines
|
|
75
|
+
- `=` - Auto-format selection
|
|
76
|
+
|
|
77
|
+
**Duplicate lines:**
|
|
78
|
+
- `yyp` - Yank current line and paste below
|
|
79
|
+
- `5yyp` - Copy current line 5 times below
|
|
80
|
+
- `Vj:t.` - Duplicate visual selection
|
|
81
|
+
|
|
82
|
+
}}}
|
|
83
|
+
|
|
84
|
+
### Search & Replace in Objects {{{
|
|
85
|
+
|
|
86
|
+
**Find all properties:**
|
|
87
|
+
```vim
|
|
88
|
+
/\w\+: " Find all property names (word + colon)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Replace property names:**
|
|
92
|
+
```vim
|
|
93
|
+
:%s/nombre/name/g " Replace 'nombre' with 'name' globally
|
|
94
|
+
:%s/\(\w\+\):/"\1":/g " Add quotes around all property names
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Multi-cursor editing:**
|
|
98
|
+
- `<C-n>` - Start multi-cursor on word under cursor
|
|
99
|
+
- `<C-n>` again - Add next match
|
|
100
|
+
- `<C-x>` - Skip current match
|
|
101
|
+
- Type to edit all at once
|
|
102
|
+
|
|
103
|
+
}}}
|
|
104
|
+
|
|
105
|
+
}}}
|
|
106
|
+
|
|
107
|
+
## Level 1: Object Fundamentals {{{
|
|
108
|
+
|
|
109
|
+
### Warmup 1.1: Object Access {{{
|
|
110
|
+
|
|
111
|
+
**Exercise:**
|
|
112
|
+
```javascript
|
|
113
|
+
const product = {
|
|
114
|
+
id: 101,
|
|
115
|
+
name: "Laptop",
|
|
116
|
+
price: 15000,
|
|
117
|
+
inStock: true
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// TODO: Access and print:
|
|
121
|
+
// 1. Product name
|
|
122
|
+
// 2. Product price
|
|
123
|
+
// 3. Check if in stock
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Solution:**
|
|
127
|
+
```javascript
|
|
128
|
+
const product = {
|
|
129
|
+
id: 101,
|
|
130
|
+
name: "Laptop",
|
|
131
|
+
price: 15000,
|
|
132
|
+
inStock: true
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(product.name) // "Laptop"
|
|
136
|
+
console.log(product.price) // 15000
|
|
137
|
+
console.log(product.inStock) // true
|
|
138
|
+
|
|
139
|
+
// Alternative: bracket notation
|
|
140
|
+
console.log(product['name']) // "Laptop"
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**LazyVim Tip:** Place cursor on `product` and press `gd` to jump to definition.
|
|
144
|
+
|
|
145
|
+
}}}
|
|
146
|
+
|
|
147
|
+
### Warmup 1.2: Object Modification {{{
|
|
148
|
+
|
|
149
|
+
**Exercise:**
|
|
150
|
+
```javascript
|
|
151
|
+
const user = {
|
|
152
|
+
username: "ana_dev",
|
|
153
|
+
email: "ana@example.com",
|
|
154
|
+
verified: false
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// TODO:
|
|
158
|
+
// 1. Change verified to true
|
|
159
|
+
// 2. Add a new property: role = "developer"
|
|
160
|
+
// 3. Delete the email property
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Solution:**
|
|
164
|
+
```javascript
|
|
165
|
+
const user = {
|
|
166
|
+
username: "ana_dev",
|
|
167
|
+
email: "ana@example.com",
|
|
168
|
+
verified: false
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 1. Modify property
|
|
172
|
+
user.verified = true
|
|
173
|
+
|
|
174
|
+
// 2. Add new property
|
|
175
|
+
user.role = "developer"
|
|
176
|
+
|
|
177
|
+
// 3. Delete property
|
|
178
|
+
delete user.email
|
|
179
|
+
|
|
180
|
+
console.log(user)
|
|
181
|
+
// { username: "ana_dev", verified: true, role: "developer" }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**LazyVim Tip:** Use `ciw` (change inner word) to quickly edit property values.
|
|
185
|
+
|
|
186
|
+
}}}
|
|
187
|
+
|
|
188
|
+
### Warmup 1.3: Object Destructuring {{{
|
|
189
|
+
|
|
190
|
+
**Exercise:**
|
|
191
|
+
```javascript
|
|
192
|
+
const config = {
|
|
193
|
+
host: "localhost",
|
|
194
|
+
port: 3000,
|
|
195
|
+
timeout: 5000,
|
|
196
|
+
retries: 3
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// TODO: Extract host and port using destructuring
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Solution:**
|
|
203
|
+
```javascript
|
|
204
|
+
const config = {
|
|
205
|
+
host: "localhost",
|
|
206
|
+
port: 3000,
|
|
207
|
+
timeout: 5000,
|
|
208
|
+
retries: 3
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Basic destructuring
|
|
212
|
+
const { host, port } = config
|
|
213
|
+
console.log(host) // "localhost"
|
|
214
|
+
console.log(port) // 3000
|
|
215
|
+
|
|
216
|
+
// With renaming
|
|
217
|
+
const { host: serverHost, port: serverPort } = config
|
|
218
|
+
console.log(serverHost) // "localhost"
|
|
219
|
+
|
|
220
|
+
// With default values
|
|
221
|
+
const { ssl = false } = config
|
|
222
|
+
console.log(ssl) // false (property doesn't exist)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**LazyVim Tip:** Use `%` to jump between matching braces when editing destructuring.
|
|
226
|
+
|
|
227
|
+
}}}
|
|
228
|
+
|
|
229
|
+
### Warmup 1.4: Nested Objects {{{
|
|
230
|
+
|
|
231
|
+
**Exercise:**
|
|
232
|
+
```javascript
|
|
233
|
+
const developer = {
|
|
234
|
+
name: "Carlos",
|
|
235
|
+
skills: {
|
|
236
|
+
languages: ["JavaScript", "Python"],
|
|
237
|
+
frameworks: ["React", "Express"]
|
|
238
|
+
},
|
|
239
|
+
experience: {
|
|
240
|
+
years: 3,
|
|
241
|
+
level: "mid"
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// TODO:
|
|
246
|
+
// 1. Access the first language
|
|
247
|
+
// 2. Access experience level
|
|
248
|
+
// 3. Add "Node.js" to frameworks
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Solution:**
|
|
252
|
+
```javascript
|
|
253
|
+
const developer = {
|
|
254
|
+
name: "Carlos",
|
|
255
|
+
skills: {
|
|
256
|
+
languages: ["JavaScript", "Python"],
|
|
257
|
+
frameworks: ["React", "Express"]
|
|
258
|
+
},
|
|
259
|
+
experience: {
|
|
260
|
+
years: 3,
|
|
261
|
+
level: "mid"
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 1. Access nested array
|
|
266
|
+
console.log(developer.skills.languages[0]) // "JavaScript"
|
|
267
|
+
|
|
268
|
+
// 2. Access nested object
|
|
269
|
+
console.log(developer.experience.level) // "mid"
|
|
270
|
+
|
|
271
|
+
// 3. Modify nested array
|
|
272
|
+
developer.skills.frameworks.push("Node.js")
|
|
273
|
+
console.log(developer.skills.frameworks)
|
|
274
|
+
// ["React", "Express", "Node.js"]
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**LazyVim Tip:** Use `vi{` to select inside object, then `=` to auto-format.
|
|
278
|
+
|
|
279
|
+
}}}
|
|
280
|
+
|
|
281
|
+
}}}
|
|
282
|
+
|
|
283
|
+
## Level 2: Array Fundamentals {{{
|
|
284
|
+
|
|
285
|
+
### Warmup 2.1: Array.map() - Transform {{{
|
|
286
|
+
|
|
287
|
+
**Exercise:**
|
|
288
|
+
```javascript
|
|
289
|
+
const prices = [100, 250, 500, 1000]
|
|
290
|
+
|
|
291
|
+
// TODO: Create new array with 16% tax added to each price
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
**Solution:**
|
|
295
|
+
```javascript
|
|
296
|
+
const prices = [100, 250, 500, 1000]
|
|
297
|
+
|
|
298
|
+
const pricesWithTax = prices.map(price => price * 1.16)
|
|
299
|
+
console.log(pricesWithTax)
|
|
300
|
+
// [116, 290, 580, 1160]
|
|
301
|
+
|
|
302
|
+
// Longer form (same result):
|
|
303
|
+
const pricesWithTax2 = prices.map(function(price) {
|
|
304
|
+
return price * 1.16
|
|
305
|
+
})
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**LazyVim Tip:** Select the array with `vi[` and press `>` to indent for readability.
|
|
309
|
+
|
|
310
|
+
}}}
|
|
311
|
+
|
|
312
|
+
### Warmup 2.2: Array.filter() - Select {{{
|
|
313
|
+
|
|
314
|
+
**Exercise:**
|
|
315
|
+
```javascript
|
|
316
|
+
const numbers = [5, 12, 8, 130, 44, 3, 21]
|
|
317
|
+
|
|
318
|
+
// TODO:
|
|
319
|
+
// 1. Get numbers greater than 10
|
|
320
|
+
// 2. Get even numbers only
|
|
321
|
+
// 3. Get numbers between 10 and 50
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Solution:**
|
|
325
|
+
```javascript
|
|
326
|
+
const numbers = [5, 12, 8, 130, 44, 3, 21]
|
|
327
|
+
|
|
328
|
+
// 1. Greater than 10
|
|
329
|
+
const greaterThan10 = numbers.filter(num => num > 10)
|
|
330
|
+
console.log(greaterThan10) // [12, 130, 44, 21]
|
|
331
|
+
|
|
332
|
+
// 2. Even numbers
|
|
333
|
+
const evens = numbers.filter(num => num % 2 === 0)
|
|
334
|
+
console.log(evens) // [12, 8, 130, 44]
|
|
335
|
+
|
|
336
|
+
// 3. Between 10 and 50
|
|
337
|
+
const between = numbers.filter(num => num >= 10 && num <= 50)
|
|
338
|
+
console.log(between) // [12, 44, 21]
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**LazyVim Tip:** Use `<C-n>` on `num` to select all occurrences and edit simultaneously.
|
|
342
|
+
|
|
343
|
+
}}}
|
|
344
|
+
|
|
345
|
+
### Warmup 2.3: Array.find() - Search {{{
|
|
346
|
+
|
|
347
|
+
**Exercise:**
|
|
348
|
+
```javascript
|
|
349
|
+
const users = ["ana", "carlos", "diana", "eduardo"]
|
|
350
|
+
|
|
351
|
+
// TODO:
|
|
352
|
+
// 1. Find first name starting with 'd'
|
|
353
|
+
// 2. Find first name longer than 5 characters
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Solution:**
|
|
357
|
+
```javascript
|
|
358
|
+
const users = ["ana", "carlos", "diana", "eduardo"]
|
|
359
|
+
|
|
360
|
+
// 1. Starts with 'd'
|
|
361
|
+
const startsWithD = users.find(name => name.startsWith('d'))
|
|
362
|
+
console.log(startsWithD) // "diana"
|
|
363
|
+
|
|
364
|
+
// 2. Longer than 5 characters
|
|
365
|
+
const longName = users.find(name => name.length > 5)
|
|
366
|
+
console.log(longName) // "carlos"
|
|
367
|
+
|
|
368
|
+
// Note: find() returns first match or undefined
|
|
369
|
+
const notFound = users.find(name => name.startsWith('z'))
|
|
370
|
+
console.log(notFound) // undefined
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**LazyVim Tip:** Use `gd` on variable name to see all usages in file.
|
|
374
|
+
|
|
375
|
+
}}}
|
|
376
|
+
|
|
377
|
+
### Warmup 2.4: Array.includes() - Check {{{
|
|
378
|
+
|
|
379
|
+
**Exercise:**
|
|
380
|
+
```javascript
|
|
381
|
+
const validRoles = ["admin", "editor", "viewer"]
|
|
382
|
+
|
|
383
|
+
// TODO: Check if "editor" and "superadmin" are valid roles
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
**Solution:**
|
|
387
|
+
```javascript
|
|
388
|
+
const validRoles = ["admin", "editor", "viewer"]
|
|
389
|
+
|
|
390
|
+
console.log(validRoles.includes("editor")) // true
|
|
391
|
+
console.log(validRoles.includes("superadmin")) // false
|
|
392
|
+
|
|
393
|
+
// Use case: validation
|
|
394
|
+
function isValidRole(role) {
|
|
395
|
+
return validRoles.includes(role)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
console.log(isValidRole("admin")) // true
|
|
399
|
+
console.log(isValidRole("hacker")) // false
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
}}}
|
|
403
|
+
|
|
404
|
+
}}}
|
|
405
|
+
|
|
406
|
+
## Level 3: Array of Objects {{{
|
|
407
|
+
|
|
408
|
+
### Warmup 3.1: Filter Objects by Property {{{
|
|
409
|
+
|
|
410
|
+
**Exercise:**
|
|
411
|
+
```javascript
|
|
412
|
+
const products = [
|
|
413
|
+
{ id: 1, name: "Mouse", price: 200, category: "electronics" },
|
|
414
|
+
{ id: 2, name: "Teclado", price: 500, category: "electronics" },
|
|
415
|
+
{ id: 3, name: "Silla", price: 1500, category: "furniture" },
|
|
416
|
+
{ id: 4, name: "Monitor", price: 3000, category: "electronics" }
|
|
417
|
+
]
|
|
418
|
+
|
|
419
|
+
// TODO:
|
|
420
|
+
// 1. Get all electronics
|
|
421
|
+
// 2. Get products under $1000
|
|
422
|
+
// 3. Get products with price between 500 and 2000
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**Solution:**
|
|
426
|
+
```javascript
|
|
427
|
+
const products = [
|
|
428
|
+
{ id: 1, name: "Mouse", price: 200, category: "electronics" },
|
|
429
|
+
{ id: 2, name: "Teclado", price: 500, category: "electronics" },
|
|
430
|
+
{ id: 3, name: "Silla", price: 1500, category: "furniture" },
|
|
431
|
+
{ id: 4, name: "Monitor", price: 3000, category: "electronics" }
|
|
432
|
+
]
|
|
433
|
+
|
|
434
|
+
// 1. All electronics
|
|
435
|
+
const electronics = products.filter(p => p.category === "electronics")
|
|
436
|
+
console.log(electronics)
|
|
437
|
+
// [Mouse, Teclado, Monitor]
|
|
438
|
+
|
|
439
|
+
// 2. Under $1000
|
|
440
|
+
const affordable = products.filter(p => p.price < 1000)
|
|
441
|
+
console.log(affordable)
|
|
442
|
+
// [Mouse, Teclado]
|
|
443
|
+
|
|
444
|
+
// 3. Between 500 and 2000
|
|
445
|
+
const midRange = products.filter(p => p.price >= 500 && p.price <= 2000)
|
|
446
|
+
console.log(midRange)
|
|
447
|
+
// [Teclado, Silla]
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**LazyVim Tip:** Use `va{` to select entire object, then `p` to paste elsewhere.
|
|
451
|
+
|
|
452
|
+
}}}
|
|
453
|
+
|
|
454
|
+
### Warmup 3.2: Map Objects to Extract Values {{{
|
|
455
|
+
|
|
456
|
+
**Exercise:**
|
|
457
|
+
```javascript
|
|
458
|
+
const students = [
|
|
459
|
+
{ name: "Ana", grade: 85, passed: true },
|
|
460
|
+
{ name: "Bruno", grade: 92, passed: true },
|
|
461
|
+
{ name: "Carla", grade: 68, passed: false },
|
|
462
|
+
{ name: "Diego", grade: 78, passed: true }
|
|
463
|
+
]
|
|
464
|
+
|
|
465
|
+
// TODO:
|
|
466
|
+
// 1. Get array of just names
|
|
467
|
+
// 2. Get array of just grades
|
|
468
|
+
// 3. Get array of objects with name + grade only
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
**Solution:**
|
|
472
|
+
```javascript
|
|
473
|
+
const students = [
|
|
474
|
+
{ name: "Ana", grade: 85, passed: true },
|
|
475
|
+
{ name: "Bruno", grade: 92, passed: true },
|
|
476
|
+
{ name: "Carla", grade: 68, passed: false },
|
|
477
|
+
{ name: "Diego", grade: 78, passed: true }
|
|
478
|
+
]
|
|
479
|
+
|
|
480
|
+
// 1. Just names
|
|
481
|
+
const names = students.map(s => s.name)
|
|
482
|
+
console.log(names)
|
|
483
|
+
// ["Ana", "Bruno", "Carla", "Diego"]
|
|
484
|
+
|
|
485
|
+
// 2. Just grades
|
|
486
|
+
const grades = students.map(s => s.grade)
|
|
487
|
+
console.log(grades)
|
|
488
|
+
// [85, 92, 68, 78]
|
|
489
|
+
|
|
490
|
+
// 3. Name + grade objects
|
|
491
|
+
const simplified = students.map(s => ({ name: s.name, grade: s.grade }))
|
|
492
|
+
console.log(simplified)
|
|
493
|
+
// [{ name: "Ana", grade: 85 }, ...]
|
|
494
|
+
|
|
495
|
+
// Or using destructuring:
|
|
496
|
+
const simplified2 = students.map(({ name, grade }) => ({ name, grade }))
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**LazyVim Tip:** Use `ci{` to change object content quickly.
|
|
500
|
+
|
|
501
|
+
}}}
|
|
502
|
+
|
|
503
|
+
### Warmup 3.3: Find Object by Property {{{
|
|
504
|
+
|
|
505
|
+
**Exercise:**
|
|
506
|
+
```javascript
|
|
507
|
+
const employees = [
|
|
508
|
+
{ id: 101, name: "Laura", department: "Engineering" },
|
|
509
|
+
{ id: 102, name: "Miguel", department: "Sales" },
|
|
510
|
+
{ id: 103, name: "Sofia", department: "Engineering" },
|
|
511
|
+
{ id: 104, name: "Pedro", department: "HR" }
|
|
512
|
+
]
|
|
513
|
+
|
|
514
|
+
// TODO:
|
|
515
|
+
// 1. Find employee with id 103
|
|
516
|
+
// 2. Find first employee in Engineering
|
|
517
|
+
// 3. Find employee named "Pedro"
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
**Solution:**
|
|
521
|
+
```javascript
|
|
522
|
+
const employees = [
|
|
523
|
+
{ id: 101, name: "Laura", department: "Engineering" },
|
|
524
|
+
{ id: 102, name: "Miguel", department: "Sales" },
|
|
525
|
+
{ id: 103, name: "Sofia", department: "Engineering" },
|
|
526
|
+
{ id: 104, name: "Pedro", department: "HR" }
|
|
527
|
+
]
|
|
528
|
+
|
|
529
|
+
// 1. By ID
|
|
530
|
+
const employee = employees.find(e => e.id === 103)
|
|
531
|
+
console.log(employee)
|
|
532
|
+
// { id: 103, name: "Sofia", department: "Engineering" }
|
|
533
|
+
|
|
534
|
+
// 2. First in Engineering
|
|
535
|
+
const engineer = employees.find(e => e.department === "Engineering")
|
|
536
|
+
console.log(engineer)
|
|
537
|
+
// { id: 101, name: "Laura", department: "Engineering" }
|
|
538
|
+
|
|
539
|
+
// 3. By name
|
|
540
|
+
const pedro = employees.find(e => e.name === "Pedro")
|
|
541
|
+
console.log(pedro)
|
|
542
|
+
// { id: 104, name: "Pedro", department: "HR" }
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
}}}
|
|
546
|
+
|
|
547
|
+
### Warmup 3.4: Sort Array of Objects {{{
|
|
548
|
+
|
|
549
|
+
**Exercise:**
|
|
550
|
+
```javascript
|
|
551
|
+
const tasks = [
|
|
552
|
+
{ title: "Fix bug", priority: 3, completed: false },
|
|
553
|
+
{ title: "Write docs", priority: 1, completed: false },
|
|
554
|
+
{ title: "Deploy", priority: 2, completed: true },
|
|
555
|
+
{ title: "Review PR", priority: 3, completed: false }
|
|
556
|
+
]
|
|
557
|
+
|
|
558
|
+
// TODO:
|
|
559
|
+
// 1. Sort by priority (lowest first)
|
|
560
|
+
// 2. Sort by priority (highest first)
|
|
561
|
+
// 3. Sort by title alphabetically
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
**Solution:**
|
|
565
|
+
```javascript
|
|
566
|
+
const tasks = [
|
|
567
|
+
{ title: "Fix bug", priority: 3, completed: false },
|
|
568
|
+
{ title: "Write docs", priority: 1, completed: false },
|
|
569
|
+
{ title: "Deploy", priority: 2, completed: true },
|
|
570
|
+
{ title: "Review PR", priority: 3, completed: false }
|
|
571
|
+
]
|
|
572
|
+
|
|
573
|
+
// 1. Priority ascending (lowest first)
|
|
574
|
+
const sortedAsc = [...tasks].sort((a, b) => a.priority - b.priority)
|
|
575
|
+
console.log(sortedAsc.map(t => `${t.title} (${t.priority})`))
|
|
576
|
+
// ["Write docs (1)", "Deploy (2)", "Fix bug (3)", "Review PR (3)"]
|
|
577
|
+
|
|
578
|
+
// 2. Priority descending (highest first)
|
|
579
|
+
const sortedDesc = [...tasks].sort((a, b) => b.priority - a.priority)
|
|
580
|
+
console.log(sortedDesc.map(t => `${t.title} (${t.priority})`))
|
|
581
|
+
// ["Fix bug (3)", "Review PR (3)", "Deploy (2)", "Write docs (1)"]
|
|
582
|
+
|
|
583
|
+
// 3. Alphabetically by title
|
|
584
|
+
const sortedAlpha = [...tasks].sort((a, b) => a.title.localeCompare(b.title))
|
|
585
|
+
console.log(sortedAlpha.map(t => t.title))
|
|
586
|
+
// ["Deploy", "Fix bug", "Review PR", "Write docs"]
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
**Note:** `[...tasks]` creates a copy because `.sort()` mutates the original array.
|
|
590
|
+
|
|
591
|
+
**LazyVim Tip:** Use `V` to select line, then `:!node` to run just that line in Node.
|
|
592
|
+
|
|
593
|
+
}}}
|
|
594
|
+
|
|
595
|
+
}}}
|
|
596
|
+
|
|
597
|
+
## Level 4: Advanced Patterns {{{
|
|
598
|
+
|
|
599
|
+
### Warmup 4.1: Array.reduce() - Sum & Count {{{
|
|
600
|
+
|
|
601
|
+
**Exercise:**
|
|
602
|
+
```javascript
|
|
603
|
+
const cart = [
|
|
604
|
+
{ product: "Mouse", price: 200, quantity: 2 },
|
|
605
|
+
{ product: "Teclado", price: 500, quantity: 1 },
|
|
606
|
+
{ product: "Monitor", price: 3000, quantity: 1 }
|
|
607
|
+
]
|
|
608
|
+
|
|
609
|
+
// TODO:
|
|
610
|
+
// 1. Calculate total price (price * quantity for all items)
|
|
611
|
+
// 2. Count total items in cart
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
**Solution:**
|
|
615
|
+
```javascript
|
|
616
|
+
const cart = [
|
|
617
|
+
{ product: "Mouse", price: 200, quantity: 2 },
|
|
618
|
+
{ product: "Teclado", price: 500, quantity: 1 },
|
|
619
|
+
{ product: "Monitor", price: 3000, quantity: 1 }
|
|
620
|
+
]
|
|
621
|
+
|
|
622
|
+
// 1. Total price
|
|
623
|
+
const total = cart.reduce((sum, item) => {
|
|
624
|
+
return sum + (item.price * item.quantity)
|
|
625
|
+
}, 0)
|
|
626
|
+
console.log(total) // 3900 (400 + 500 + 3000)
|
|
627
|
+
|
|
628
|
+
// Shorter version:
|
|
629
|
+
const total2 = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0)
|
|
630
|
+
|
|
631
|
+
// 2. Total items
|
|
632
|
+
const totalItems = cart.reduce((count, item) => count + item.quantity, 0)
|
|
633
|
+
console.log(totalItems) // 4 (2 + 1 + 1)
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
**How reduce works:**
|
|
637
|
+
```javascript
|
|
638
|
+
// reduce((accumulator, currentItem) => newAccumulator, initialValue)
|
|
639
|
+
// ↑ ↑ ↑ ↑
|
|
640
|
+
// previous current item what to starting
|
|
641
|
+
// result return next value
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
}}}
|
|
645
|
+
|
|
646
|
+
### Warmup 4.2: Group By Property {{{
|
|
647
|
+
|
|
648
|
+
**Exercise:**
|
|
649
|
+
```javascript
|
|
650
|
+
const transactions = [
|
|
651
|
+
{ id: 1, type: "income", amount: 5000 },
|
|
652
|
+
{ id: 2, type: "expense", amount: 1200 },
|
|
653
|
+
{ id: 3, type: "income", amount: 3000 },
|
|
654
|
+
{ id: 4, type: "expense", amount: 800 },
|
|
655
|
+
{ id: 5, type: "income", amount: 2000 }
|
|
656
|
+
]
|
|
657
|
+
|
|
658
|
+
// TODO: Group transactions by type
|
|
659
|
+
// Result: { income: [...], expense: [...] }
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
**Solution:**
|
|
663
|
+
```javascript
|
|
664
|
+
const transactions = [
|
|
665
|
+
{ id: 1, type: "income", amount: 5000 },
|
|
666
|
+
{ id: 2, type: "expense", amount: 1200 },
|
|
667
|
+
{ id: 3, type: "income", amount: 3000 },
|
|
668
|
+
{ id: 4, type: "expense", amount: 800 },
|
|
669
|
+
{ id: 5, type: "income", amount: 2000 }
|
|
670
|
+
]
|
|
671
|
+
|
|
672
|
+
const grouped = transactions.reduce((acc, transaction) => {
|
|
673
|
+
const type = transaction.type
|
|
674
|
+
|
|
675
|
+
// Create array if doesn't exist
|
|
676
|
+
if (!acc[type]) {
|
|
677
|
+
acc[type] = []
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Add transaction to appropriate group
|
|
681
|
+
acc[type].push(transaction)
|
|
682
|
+
|
|
683
|
+
return acc
|
|
684
|
+
}, {})
|
|
685
|
+
|
|
686
|
+
console.log(grouped)
|
|
687
|
+
/*
|
|
688
|
+
{
|
|
689
|
+
income: [
|
|
690
|
+
{ id: 1, type: "income", amount: 5000 },
|
|
691
|
+
{ id: 3, type: "income", amount: 3000 },
|
|
692
|
+
{ id: 5, type: "income", amount: 2000 }
|
|
693
|
+
],
|
|
694
|
+
expense: [
|
|
695
|
+
{ id: 2, type: "expense", amount: 1200 },
|
|
696
|
+
{ id: 4, type: "expense", amount: 800 }
|
|
697
|
+
]
|
|
698
|
+
}
|
|
699
|
+
*/
|
|
700
|
+
|
|
701
|
+
// Calculate totals per group:
|
|
702
|
+
const totals = Object.entries(grouped).map(([type, items]) => ({
|
|
703
|
+
type,
|
|
704
|
+
total: items.reduce((sum, item) => sum + item.amount, 0)
|
|
705
|
+
}))
|
|
706
|
+
console.log(totals)
|
|
707
|
+
// [{ type: "income", total: 10000 }, { type: "expense", total: 2000 }]
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
**LazyVim Tip:** Use `=i{` to auto-format the grouped object for readability.
|
|
711
|
+
|
|
712
|
+
}}}
|
|
713
|
+
|
|
714
|
+
### Warmup 4.3: Chain Multiple Methods {{{
|
|
715
|
+
|
|
716
|
+
**Exercise:**
|
|
717
|
+
```javascript
|
|
718
|
+
const orders = [
|
|
719
|
+
{ id: 1, customer: "Ana", total: 500, status: "completed" },
|
|
720
|
+
{ id: 2, customer: "Bruno", total: 1200, status: "completed" },
|
|
721
|
+
{ id: 3, customer: "Carla", total: 300, status: "pending" },
|
|
722
|
+
{ id: 4, customer: "Diana", total: 2000, status: "completed" },
|
|
723
|
+
{ id: 5, customer: "Eduardo", total: 150, status: "cancelled" }
|
|
724
|
+
]
|
|
725
|
+
|
|
726
|
+
// TODO: Get total revenue from completed orders over $500
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
**Solution:**
|
|
730
|
+
```javascript
|
|
731
|
+
const orders = [
|
|
732
|
+
{ id: 1, customer: "Ana", total: 500, status: "completed" },
|
|
733
|
+
{ id: 2, customer: "Bruno", total: 1200, status: "completed" },
|
|
734
|
+
{ id: 3, customer: "Carla", total: 300, status: "pending" },
|
|
735
|
+
{ id: 4, customer: "Diana", total: 2000, status: "completed" },
|
|
736
|
+
{ id: 5, customer: "Eduardo", total: 150, status: "cancelled" }
|
|
737
|
+
]
|
|
738
|
+
|
|
739
|
+
const revenue = orders
|
|
740
|
+
.filter(order => order.status === "completed") // Only completed
|
|
741
|
+
.filter(order => order.total > 500) // Over $500
|
|
742
|
+
.reduce((sum, order) => sum + order.total, 0) // Sum totals
|
|
743
|
+
|
|
744
|
+
console.log(revenue) // 3200 (1200 + 2000)
|
|
745
|
+
|
|
746
|
+
// Alternative: Single filter
|
|
747
|
+
const revenue2 = orders
|
|
748
|
+
.filter(o => o.status === "completed" && o.total > 500)
|
|
749
|
+
.reduce((sum, o) => sum + o.total, 0)
|
|
750
|
+
|
|
751
|
+
// Step by step visualization:
|
|
752
|
+
const completed = orders.filter(o => o.status === "completed")
|
|
753
|
+
console.log("Completed:", completed.length) // 3
|
|
754
|
+
|
|
755
|
+
const highValue = completed.filter(o => o.total > 500)
|
|
756
|
+
console.log("High value:", highValue.length) // 2
|
|
757
|
+
|
|
758
|
+
const total = highValue.reduce((sum, o) => sum + o.total, 0)
|
|
759
|
+
console.log("Total:", total) // 3200
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
**LazyVim Tip:** Use `Vj` to select multiple lines, then `>` to indent for readability.
|
|
763
|
+
|
|
764
|
+
}}}
|
|
765
|
+
|
|
766
|
+
### Warmup 4.4: Build Lookup Object {{{
|
|
767
|
+
|
|
768
|
+
**Exercise:**
|
|
769
|
+
```javascript
|
|
770
|
+
const products = [
|
|
771
|
+
{ sku: "MSE-001", name: "Mouse", price: 200 },
|
|
772
|
+
{ sku: "KBD-002", name: "Teclado", price: 500 },
|
|
773
|
+
{ sku: "MON-003", name: "Monitor", price: 3000 }
|
|
774
|
+
]
|
|
775
|
+
|
|
776
|
+
// TODO: Create lookup object where sku is key
|
|
777
|
+
// Result: { "MSE-001": {...}, "KBD-002": {...}, ... }
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
**Solution:**
|
|
781
|
+
```javascript
|
|
782
|
+
const products = [
|
|
783
|
+
{ sku: "MSE-001", name: "Mouse", price: 200 },
|
|
784
|
+
{ sku: "KBD-002", name: "Teclado", price: 500 },
|
|
785
|
+
{ sku: "MON-003", name: "Monitor", price: 3000 }
|
|
786
|
+
]
|
|
787
|
+
|
|
788
|
+
// Method 1: Using reduce
|
|
789
|
+
const lookup = products.reduce((acc, product) => {
|
|
790
|
+
acc[product.sku] = product
|
|
791
|
+
return acc
|
|
792
|
+
}, {})
|
|
793
|
+
|
|
794
|
+
console.log(lookup)
|
|
795
|
+
/*
|
|
796
|
+
{
|
|
797
|
+
"MSE-001": { sku: "MSE-001", name: "Mouse", price: 200 },
|
|
798
|
+
"KBD-002": { sku: "KBD-002", name: "Teclado", price: 500 },
|
|
799
|
+
"MON-003": { sku: "MON-003", name: "Monitor", price: 3000 }
|
|
800
|
+
}
|
|
801
|
+
*/
|
|
802
|
+
|
|
803
|
+
// Usage: Fast lookups by SKU
|
|
804
|
+
console.log(lookup["KBD-002"].price) // 500
|
|
805
|
+
|
|
806
|
+
// Method 2: Using Object.fromEntries (modern)
|
|
807
|
+
const lookup2 = Object.fromEntries(
|
|
808
|
+
products.map(p => [p.sku, p])
|
|
809
|
+
)
|
|
810
|
+
|
|
811
|
+
// Why this is useful:
|
|
812
|
+
// Array.find() = O(n) - has to search
|
|
813
|
+
// Object lookup = O(1) - instant access
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
}}}
|
|
817
|
+
|
|
818
|
+
### Warmup 4.5: Flatten Nested Arrays {{{
|
|
819
|
+
|
|
820
|
+
**Exercise:**
|
|
821
|
+
```javascript
|
|
822
|
+
const teams = [
|
|
823
|
+
{ name: "Frontend", members: ["Ana", "Bruno"] },
|
|
824
|
+
{ name: "Backend", members: ["Carla", "Diego", "Elena"] },
|
|
825
|
+
{ name: "DevOps", members: ["Franco"] }
|
|
826
|
+
]
|
|
827
|
+
|
|
828
|
+
// TODO: Get a single array of all member names
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
**Solution:**
|
|
832
|
+
```javascript
|
|
833
|
+
const teams = [
|
|
834
|
+
{ name: "Frontend", members: ["Ana", "Bruno"] },
|
|
835
|
+
{ name: "Backend", members: ["Carla", "Diego", "Elena"] },
|
|
836
|
+
{ name: "DevOps", members: ["Franco"] }
|
|
837
|
+
]
|
|
838
|
+
|
|
839
|
+
// Method 1: flatMap (recommended)
|
|
840
|
+
const allMembers = teams.flatMap(team => team.members)
|
|
841
|
+
console.log(allMembers)
|
|
842
|
+
// ["Ana", "Bruno", "Carla", "Diego", "Elena", "Franco"]
|
|
843
|
+
|
|
844
|
+
// Method 2: map + flat
|
|
845
|
+
const allMembers2 = teams.map(team => team.members).flat()
|
|
846
|
+
|
|
847
|
+
// Method 3: reduce + concat
|
|
848
|
+
const allMembers3 = teams.reduce((all, team) => {
|
|
849
|
+
return all.concat(team.members)
|
|
850
|
+
}, [])
|
|
851
|
+
|
|
852
|
+
// Advanced: Include team name
|
|
853
|
+
const withTeams = teams.flatMap(team =>
|
|
854
|
+
team.members.map(member => ({
|
|
855
|
+
name: member,
|
|
856
|
+
team: team.name
|
|
857
|
+
}))
|
|
858
|
+
)
|
|
859
|
+
console.log(withTeams)
|
|
860
|
+
/*
|
|
861
|
+
[
|
|
862
|
+
{ name: "Ana", team: "Frontend" },
|
|
863
|
+
{ name: "Bruno", team: "Frontend" },
|
|
864
|
+
{ name: "Carla", team: "Backend" },
|
|
865
|
+
...
|
|
866
|
+
]
|
|
867
|
+
*/
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
}}}
|
|
871
|
+
|
|
872
|
+
### Warmup 4.6: Complex Transformation {{{
|
|
873
|
+
|
|
874
|
+
**Exercise:**
|
|
875
|
+
```javascript
|
|
876
|
+
const rawData = [
|
|
877
|
+
{ userId: 1, purchaseDate: "2024-01-15", amount: 500 },
|
|
878
|
+
{ userId: 2, purchaseDate: "2024-01-16", amount: 1200 },
|
|
879
|
+
{ userId: 1, purchaseDate: "2024-02-01", amount: 300 },
|
|
880
|
+
{ userId: 3, purchaseDate: "2024-02-05", amount: 2000 },
|
|
881
|
+
{ userId: 2, purchaseDate: "2024-02-10", amount: 800 }
|
|
882
|
+
]
|
|
883
|
+
|
|
884
|
+
// TODO: Transform to:
|
|
885
|
+
// [
|
|
886
|
+
// { userId: 1, totalSpent: 800, purchases: 2 },
|
|
887
|
+
// { userId: 2, totalSpent: 2000, purchases: 2 },
|
|
888
|
+
// { userId: 3, totalSpent: 2000, purchases: 1 }
|
|
889
|
+
// ]
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
**Solution:**
|
|
893
|
+
```javascript
|
|
894
|
+
const rawData = [
|
|
895
|
+
{ userId: 1, purchaseDate: "2024-01-15", amount: 500 },
|
|
896
|
+
{ userId: 2, purchaseDate: "2024-01-16", amount: 1200 },
|
|
897
|
+
{ userId: 1, purchaseDate: "2024-02-01", amount: 300 },
|
|
898
|
+
{ userId: 3, purchaseDate: "2024-02-05", amount: 2000 },
|
|
899
|
+
{ userId: 2, purchaseDate: "2024-02-10", amount: 800 }
|
|
900
|
+
]
|
|
901
|
+
|
|
902
|
+
// Step 1: Group by userId
|
|
903
|
+
const grouped = rawData.reduce((acc, purchase) => {
|
|
904
|
+
const id = purchase.userId
|
|
905
|
+
|
|
906
|
+
if (!acc[id]) {
|
|
907
|
+
acc[id] = []
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
acc[id].push(purchase)
|
|
911
|
+
return acc
|
|
912
|
+
}, {})
|
|
913
|
+
|
|
914
|
+
// Step 2: Transform to summary
|
|
915
|
+
const summary = Object.entries(grouped).map(([userId, purchases]) => ({
|
|
916
|
+
userId: parseInt(userId),
|
|
917
|
+
totalSpent: purchases.reduce((sum, p) => sum + p.amount, 0),
|
|
918
|
+
purchases: purchases.length
|
|
919
|
+
}))
|
|
920
|
+
|
|
921
|
+
console.log(summary)
|
|
922
|
+
/*
|
|
923
|
+
[
|
|
924
|
+
{ userId: 1, totalSpent: 800, purchases: 2 },
|
|
925
|
+
{ userId: 2, totalSpent: 2000, purchases: 2 },
|
|
926
|
+
{ userId: 3, totalSpent: 2000, purchases: 1 }
|
|
927
|
+
]
|
|
928
|
+
*/
|
|
929
|
+
|
|
930
|
+
// All in one chain:
|
|
931
|
+
const summary2 = Object.values(
|
|
932
|
+
rawData.reduce((acc, p) => {
|
|
933
|
+
if (!acc[p.userId]) {
|
|
934
|
+
acc[p.userId] = { userId: p.userId, totalSpent: 0, purchases: 0 }
|
|
935
|
+
}
|
|
936
|
+
acc[p.userId].totalSpent += p.amount
|
|
937
|
+
acc[p.userId].purchases += 1
|
|
938
|
+
return acc
|
|
939
|
+
}, {})
|
|
940
|
+
)
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
**LazyVim Tip:** Use `zf` in visual mode to create custom folds for complex functions.
|
|
944
|
+
|
|
945
|
+
}}}
|
|
946
|
+
|
|
947
|
+
}}}
|
|
948
|
+
|
|
949
|
+
## 🎯 Challenge Exercises {{{
|
|
950
|
+
|
|
951
|
+
### Challenge 1: E-commerce Cart {{{
|
|
952
|
+
|
|
953
|
+
**Scenario:**
|
|
954
|
+
```javascript
|
|
955
|
+
const cartItems = [
|
|
956
|
+
{ id: 1, product: "Laptop", price: 15000, quantity: 1, discount: 0.1 },
|
|
957
|
+
{ id: 2, product: "Mouse", price: 300, quantity: 2, discount: 0 },
|
|
958
|
+
{ id: 3, product: "Teclado", price: 800, quantity: 1, discount: 0.15 }
|
|
959
|
+
]
|
|
960
|
+
|
|
961
|
+
// TODO:
|
|
962
|
+
// 1. Calculate subtotal (price * quantity) for each item
|
|
963
|
+
// 2. Apply discount to each item
|
|
964
|
+
// 3. Calculate final total for entire cart
|
|
965
|
+
// 4. Find most expensive item (after discount)
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
**Your turn!** Try solving before looking at solution.
|
|
969
|
+
|
|
970
|
+
<details>
|
|
971
|
+
<summary>Solution (click to reveal)</summary>
|
|
972
|
+
|
|
973
|
+
```javascript
|
|
974
|
+
const cartItems = [
|
|
975
|
+
{ id: 1, product: "Laptop", price: 15000, quantity: 1, discount: 0.1 },
|
|
976
|
+
{ id: 2, product: "Mouse", price: 300, quantity: 2, discount: 0 },
|
|
977
|
+
{ id: 3, product: "Teclado", price: 800, quantity: 1, discount: 0.15 }
|
|
978
|
+
]
|
|
979
|
+
|
|
980
|
+
// 1 & 2: Add subtotal and discounted price
|
|
981
|
+
const withPrices = cartItems.map(item => ({
|
|
982
|
+
...item,
|
|
983
|
+
subtotal: item.price * item.quantity,
|
|
984
|
+
finalPrice: item.price * item.quantity * (1 - item.discount)
|
|
985
|
+
}))
|
|
986
|
+
|
|
987
|
+
// 3: Total
|
|
988
|
+
const total = withPrices.reduce((sum, item) => sum + item.finalPrice, 0)
|
|
989
|
+
console.log(`Total: $${total}`) // Total: $14230
|
|
990
|
+
|
|
991
|
+
// 4: Most expensive
|
|
992
|
+
const mostExpensive = withPrices.reduce((max, item) =>
|
|
993
|
+
item.finalPrice > max.finalPrice ? item : max
|
|
994
|
+
)
|
|
995
|
+
console.log(`Most expensive: ${mostExpensive.product}`) // Laptop
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
</details>
|
|
999
|
+
|
|
1000
|
+
}}}
|
|
1001
|
+
|
|
1002
|
+
### Challenge 2: Student Grade Analysis {{{
|
|
1003
|
+
|
|
1004
|
+
**Scenario:**
|
|
1005
|
+
```javascript
|
|
1006
|
+
const students = [
|
|
1007
|
+
{ name: "Ana", scores: [85, 90, 78] },
|
|
1008
|
+
{ name: "Bruno", scores: [92, 88, 95] },
|
|
1009
|
+
{ name: "Carla", scores: [70, 65, 72] },
|
|
1010
|
+
{ name: "Diego", scores: [88, 85, 90] }
|
|
1011
|
+
]
|
|
1012
|
+
|
|
1013
|
+
// TODO:
|
|
1014
|
+
// 1. Calculate average score for each student
|
|
1015
|
+
// 2. Determine pass/fail (average >= 80 = pass)
|
|
1016
|
+
// 3. Get list of students who passed
|
|
1017
|
+
// 4. Calculate class average
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
**Your turn!** Try solving before looking at solution.
|
|
1021
|
+
|
|
1022
|
+
<details>
|
|
1023
|
+
<summary>Solution (click to reveal)</summary>
|
|
1024
|
+
|
|
1025
|
+
```javascript
|
|
1026
|
+
const students = [
|
|
1027
|
+
{ name: "Ana", scores: [85, 90, 78] },
|
|
1028
|
+
{ name: "Bruno", scores: [92, 88, 95] },
|
|
1029
|
+
{ name: "Carla", scores: [70, 65, 72] },
|
|
1030
|
+
{ name: "Diego", scores: [88, 85, 90] }
|
|
1031
|
+
]
|
|
1032
|
+
|
|
1033
|
+
// 1: Calculate averages
|
|
1034
|
+
const withAverages = students.map(student => {
|
|
1035
|
+
const avg = student.scores.reduce((sum, score) => sum + score, 0) / student.scores.length
|
|
1036
|
+
return {
|
|
1037
|
+
...student,
|
|
1038
|
+
average: avg,
|
|
1039
|
+
passed: avg >= 80
|
|
1040
|
+
}
|
|
1041
|
+
})
|
|
1042
|
+
|
|
1043
|
+
// 2 & 3: Who passed?
|
|
1044
|
+
const passed = withAverages.filter(s => s.passed)
|
|
1045
|
+
console.log("Passed:", passed.map(s => s.name))
|
|
1046
|
+
// ["Ana", "Bruno", "Diego"]
|
|
1047
|
+
|
|
1048
|
+
// 4: Class average
|
|
1049
|
+
const classAvg = withAverages.reduce((sum, s) => sum + s.average, 0) / withAverages.length
|
|
1050
|
+
console.log(`Class average: ${classAvg.toFixed(2)}`)
|
|
1051
|
+
// Class average: 82.17
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
</details>
|
|
1055
|
+
|
|
1056
|
+
}}}
|
|
1057
|
+
|
|
1058
|
+
### Challenge 3: API Response Transformation {{{
|
|
1059
|
+
|
|
1060
|
+
**Scenario:**
|
|
1061
|
+
```javascript
|
|
1062
|
+
// Raw API response
|
|
1063
|
+
const apiResponse = {
|
|
1064
|
+
users: [
|
|
1065
|
+
{ id: 1, username: "ana_dev", email: "ana@example.com" },
|
|
1066
|
+
{ id: 2, username: "bruno_code", email: "bruno@example.com" }
|
|
1067
|
+
],
|
|
1068
|
+
posts: [
|
|
1069
|
+
{ id: 101, userId: 1, title: "First post", likes: 10 },
|
|
1070
|
+
{ id: 102, userId: 1, title: "Second post", likes: 5 },
|
|
1071
|
+
{ id: 103, userId: 2, title: "Bruno's post", likes: 20 }
|
|
1072
|
+
]
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// TODO: Transform to:
|
|
1076
|
+
// [
|
|
1077
|
+
// {
|
|
1078
|
+
// username: "ana_dev",
|
|
1079
|
+
// postCount: 2,
|
|
1080
|
+
// totalLikes: 15,
|
|
1081
|
+
// posts: [...]
|
|
1082
|
+
// },
|
|
1083
|
+
// {
|
|
1084
|
+
// username: "bruno_code",
|
|
1085
|
+
// postCount: 1,
|
|
1086
|
+
// totalLikes: 20,
|
|
1087
|
+
// posts: [...]
|
|
1088
|
+
// }
|
|
1089
|
+
// ]
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
**Your turn!** This is a complex one - take your time.
|
|
1093
|
+
|
|
1094
|
+
<details>
|
|
1095
|
+
<summary>Solution (click to reveal)</summary>
|
|
1096
|
+
|
|
1097
|
+
```javascript
|
|
1098
|
+
const apiResponse = {
|
|
1099
|
+
users: [
|
|
1100
|
+
{ id: 1, username: "ana_dev", email: "ana@example.com" },
|
|
1101
|
+
{ id: 2, username: "bruno_code", email: "bruno@example.com" }
|
|
1102
|
+
],
|
|
1103
|
+
posts: [
|
|
1104
|
+
{ id: 101, userId: 1, title: "First post", likes: 10 },
|
|
1105
|
+
{ id: 102, userId: 1, title: "Second post", likes: 5 },
|
|
1106
|
+
{ id: 103, userId: 2, title: "Bruno's post", likes: 20 }
|
|
1107
|
+
]
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
const transformed = apiResponse.users.map(user => {
|
|
1111
|
+
// Get posts for this user
|
|
1112
|
+
const userPosts = apiResponse.posts.filter(post => post.userId === user.id)
|
|
1113
|
+
|
|
1114
|
+
// Calculate stats
|
|
1115
|
+
const totalLikes = userPosts.reduce((sum, post) => sum + post.likes, 0)
|
|
1116
|
+
|
|
1117
|
+
return {
|
|
1118
|
+
username: user.username,
|
|
1119
|
+
postCount: userPosts.length,
|
|
1120
|
+
totalLikes: totalLikes,
|
|
1121
|
+
posts: userPosts.map(p => ({ title: p.title, likes: p.likes }))
|
|
1122
|
+
}
|
|
1123
|
+
})
|
|
1124
|
+
|
|
1125
|
+
console.log(JSON.stringify(transformed, null, 2))
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
</details>
|
|
1129
|
+
|
|
1130
|
+
}}}
|
|
1131
|
+
|
|
1132
|
+
}}}
|
|
1133
|
+
|
|
1134
|
+
## 📚 Quick Reference {{{
|
|
1135
|
+
|
|
1136
|
+
### Array Methods Cheat Sheet {{{
|
|
1137
|
+
|
|
1138
|
+
```javascript
|
|
1139
|
+
// TRANSFORM
|
|
1140
|
+
.map(item => newItem) // Transform each item
|
|
1141
|
+
|
|
1142
|
+
// FILTER
|
|
1143
|
+
.filter(item => condition) // Keep items that match
|
|
1144
|
+
|
|
1145
|
+
// FIND
|
|
1146
|
+
.find(item => condition) // First match or undefined
|
|
1147
|
+
.findIndex(item => condition) // Index of first match or -1
|
|
1148
|
+
|
|
1149
|
+
// CHECK
|
|
1150
|
+
.includes(value) // Does array contain value?
|
|
1151
|
+
.some(item => condition) // Do ANY items match?
|
|
1152
|
+
.every(item => condition) // Do ALL items match?
|
|
1153
|
+
|
|
1154
|
+
// AGGREGATE
|
|
1155
|
+
.reduce((acc, item) => newAcc, initial) // Accumulate to single value
|
|
1156
|
+
|
|
1157
|
+
// SORT
|
|
1158
|
+
.sort((a, b) => a - b) // Sort numbers ascending
|
|
1159
|
+
.sort((a, b) => b - a) // Sort numbers descending
|
|
1160
|
+
.sort((a, b) => a.localeCompare(b)) // Sort strings
|
|
1161
|
+
|
|
1162
|
+
// FLATTEN
|
|
1163
|
+
.flat() // Flatten one level
|
|
1164
|
+
.flat(2) // Flatten two levels
|
|
1165
|
+
.flatMap(item => array) // Map + flatten
|
|
1166
|
+
|
|
1167
|
+
// OTHERS
|
|
1168
|
+
.slice(start, end) // Copy portion
|
|
1169
|
+
.concat(otherArray) // Combine arrays
|
|
1170
|
+
.join(separator) // Array to string
|
|
1171
|
+
```
|
|
1172
|
+
|
|
1173
|
+
}}}
|
|
1174
|
+
|
|
1175
|
+
### Object Methods Cheat Sheet {{{
|
|
1176
|
+
|
|
1177
|
+
```javascript
|
|
1178
|
+
// KEYS & VALUES
|
|
1179
|
+
Object.keys(obj) // Array of keys
|
|
1180
|
+
Object.values(obj) // Array of values
|
|
1181
|
+
Object.entries(obj) // Array of [key, value] pairs
|
|
1182
|
+
|
|
1183
|
+
// CREATE
|
|
1184
|
+
Object.fromEntries(pairs) // Create object from [key, value] pairs
|
|
1185
|
+
Object.assign({}, obj1, obj2) // Merge objects
|
|
1186
|
+
{ ...obj1, ...obj2 } // Spread merge
|
|
1187
|
+
|
|
1188
|
+
// CHECK
|
|
1189
|
+
obj.hasOwnProperty('key') // Does property exist?
|
|
1190
|
+
'key' in obj // Does property exist? (includes inherited)
|
|
1191
|
+
|
|
1192
|
+
// TRANSFORM
|
|
1193
|
+
Object.entries(obj).map(([k, v]) => ...) // Transform to array
|
|
1194
|
+
Object.fromEntries( // Transform back to object
|
|
1195
|
+
Object.entries(obj).map(...)
|
|
1196
|
+
)
|
|
1197
|
+
```
|
|
1198
|
+
|
|
1199
|
+
}}}
|
|
1200
|
+
|
|
1201
|
+
### Common Patterns {{{
|
|
1202
|
+
|
|
1203
|
+
```javascript
|
|
1204
|
+
// PATTERN: Filter + Map
|
|
1205
|
+
const result = array
|
|
1206
|
+
.filter(item => condition)
|
|
1207
|
+
.map(item => transform)
|
|
1208
|
+
|
|
1209
|
+
// PATTERN: Group by property
|
|
1210
|
+
const grouped = array.reduce((acc, item) => {
|
|
1211
|
+
const key = item.property
|
|
1212
|
+
if (!acc[key]) acc[key] = []
|
|
1213
|
+
acc[key].push(item)
|
|
1214
|
+
return acc
|
|
1215
|
+
}, {})
|
|
1216
|
+
|
|
1217
|
+
// PATTERN: Sum property
|
|
1218
|
+
const total = array.reduce((sum, item) => sum + item.value, 0)
|
|
1219
|
+
|
|
1220
|
+
// PATTERN: Find max/min
|
|
1221
|
+
const max = array.reduce((max, item) =>
|
|
1222
|
+
item.value > max.value ? item : max
|
|
1223
|
+
)
|
|
1224
|
+
|
|
1225
|
+
// PATTERN: Create lookup
|
|
1226
|
+
const lookup = array.reduce((acc, item) => {
|
|
1227
|
+
acc[item.id] = item
|
|
1228
|
+
return acc
|
|
1229
|
+
}, {})
|
|
1230
|
+
|
|
1231
|
+
// PATTERN: Unique values
|
|
1232
|
+
const unique = [...new Set(array)]
|
|
1233
|
+
|
|
1234
|
+
// PATTERN: Count occurrences
|
|
1235
|
+
const counts = array.reduce((acc, item) => {
|
|
1236
|
+
acc[item] = (acc[item] || 0) + 1
|
|
1237
|
+
return acc
|
|
1238
|
+
}, {})
|
|
1239
|
+
```
|
|
1240
|
+
|
|
1241
|
+
}}}
|
|
1242
|
+
|
|
1243
|
+
}}}
|
|
1244
|
+
|
|
1245
|
+
## 🎓 Next Steps {{{
|
|
1246
|
+
|
|
1247
|
+
### Practice Recommendations {{{
|
|
1248
|
+
|
|
1249
|
+
1. **Daily Warmup:** Do 2-3 exercises from different levels each morning
|
|
1250
|
+
2. **Real Projects:** Apply these patterns to actual code you're writing
|
|
1251
|
+
3. **Code Reviews:** Look for opportunities to simplify loops with these methods
|
|
1252
|
+
4. **Performance:** Learn when to use forEach vs map vs reduce
|
|
1253
|
+
5. **Readability:** Chain methods for clarity, but don't overdo it
|
|
1254
|
+
|
|
1255
|
+
}}}
|
|
1256
|
+
|
|
1257
|
+
### Advanced Topics to Explore {{{
|
|
1258
|
+
|
|
1259
|
+
- **Immutability:** Why and when to avoid mutating arrays/objects
|
|
1260
|
+
- **Performance:** Big O notation for different operations
|
|
1261
|
+
- **Async Arrays:** Using Promise.all() with map
|
|
1262
|
+
- **Functional Programming:** Compose, pipe, curry
|
|
1263
|
+
- **TypeScript:** Adding types to these patterns
|
|
1264
|
+
- **Lodash/Ramda:** Utility libraries for advanced operations
|
|
1265
|
+
|
|
1266
|
+
}}}
|
|
1267
|
+
|
|
1268
|
+
### Resources {{{
|
|
1269
|
+
|
|
1270
|
+
- **MDN Docs:** [Array methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)
|
|
1271
|
+
- **Creta Lessons:** Review "Enunciados" for OOP patterns with arrays
|
|
1272
|
+
- **Practice:** [JavaScript30](https://javascript30.com/) challenges
|
|
1273
|
+
- **Reference:** Keep this file open in LazyVim while coding!
|
|
1274
|
+
|
|
1275
|
+
}}}
|
|
1276
|
+
|
|
1277
|
+
}}}
|
|
1278
|
+
|
|
1279
|
+
---
|
|
1280
|
+
|
|
1281
|
+
**Pro Tip:** Keep this file in your `.config/nvim` folder or workspace root for quick reference while coding. Use `<leader>ff` in LazyVim to fuzzy find and open it instantly! 🚀
|