2.6 מערכים מתודות מתקדמות פתרון
פתרון - מתודות מתקדמות של מערכים
תרגיל 1 - פילטור וטרנספורמציה
const products = [
{ name: "Laptop", price: 4999, category: "electronics", inStock: true },
{ name: "Shirt", price: 89, category: "clothing", inStock: true },
{ name: "Phone", price: 3499, category: "electronics", inStock: false },
{ name: "Pants", price: 159, category: "clothing", inStock: true },
{ name: "Tablet", price: 1999, category: "electronics", inStock: true },
{ name: "Hat", price: 49, category: "clothing", inStock: false },
{ name: "Monitor", price: 1499, category: "electronics", inStock: true },
{ name: "Jacket", price: 299, category: "clothing", inStock: true }
];
// 1. names of products in stock
const inStockNames = products
.filter((p) => p.inStock)
.map((p) => p.name);
console.log(inStockNames);
// ["Laptop", "Shirt", "Pants", "Tablet", "Monitor", "Jacket"]
// 2. total price of electronics
const electronicsTotal = products
.filter((p) => p.category === "electronics")
.reduce((sum, p) => sum + p.price, 0);
console.log(electronicsTotal); // 11996
// 3. clothing sorted by price (cheap to expensive)
const clothingSorted = products
.filter((p) => p.category === "clothing")
.sort((a, b) => a.price - b.price);
console.log(clothingSorted);
// [Hat(49), Shirt(89), Pants(159), Jacket(299)]
// 4. most expensive product in stock
const mostExpensiveInStock = products
.filter((p) => p.inStock)
.reduce((max, p) => p.price > max.price ? p : max);
console.log(mostExpensiveInStock); // { name: "Laptop", price: 4999, ... }
// 5. are all electronics in stock?
const allElectronicsInStock = products
.filter((p) => p.category === "electronics")
.every((p) => p.inStock);
console.log(allElectronicsInStock); // false (Phone is not in stock)
// 6. is there a product under 50?
const hasUnder50 = products.some((p) => p.price < 50);
console.log(hasUnder50); // true (Hat costs 49)
תרגיל 2 - סטטיסטיקות ציונים
const scores = [95, 82, 67, 54, 88, 91, 73, 45, 78, 99, 62, 85, 70, 38, 93];
// 1. average
const average = scores.reduce((sum, s) => sum + s, 0) / scores.length;
console.log(average); // 74.67
// 2. highest and lowest
const highest = scores.reduce((max, s) => s > max ? s : max, -Infinity);
const lowest = scores.reduce((min, s) => s < min ? s : min, Infinity);
console.log(highest, lowest); // 99, 38
// alternative using Math.max / Math.min with spread:
// const highest = Math.max(...scores);
// const lowest = Math.min(...scores);
// 3. count scores above 80
const above80 = scores.filter((s) => s > 80).length;
console.log(above80); // 6
// 4. round up to nearest multiple of 5
const rounded = scores.map((s) => Math.ceil(s / 5) * 5);
console.log(rounded);
// [95, 85, 70, 55, 90, 95, 75, 45, 80, 100, 65, 85, 70, 40, 95]
// 5. passing scores (55+) sorted high to low
const passing = scores
.filter((s) => s >= 55)
.sort((a, b) => b - a);
console.log(passing);
// [99, 95, 93, 91, 88, 85, 82, 78, 73, 70, 67, 62]
// 6. group by grade level
const grouped = scores.reduce((groups, score) => {
let level;
if (score >= 90) level = "excellent";
else if (score >= 75) level = "good";
else if (score >= 55) level = "average";
else level = "failing";
if (!groups[level]) groups[level] = [];
groups[level].push(score);
return groups;
}, {});
console.log(grouped);
// {
// excellent: [95, 91, 99, 93],
// good: [82, 88, 78, 85],
// average: [67, 73, 62, 70],
// failing: [54, 45, 38]
// }
// alternative with Object.groupBy:
const grouped2 = Object.groupBy(scores, (score) => {
if (score >= 90) return "excellent";
if (score >= 75) return "good";
if (score >= 55) return "average";
return "failing";
});
תרגיל 3 - עיבוד טקסט
const sentences = [
" Hello World ",
"JavaScript is awesome",
" arrays ARE fun ",
"I love CODING",
" Practice makes PERFECT "
];
// 1. trim and lowercase
const cleaned = sentences.map((s) => s.trim().toLowerCase());
console.log(cleaned);
// ["hello world", "javascript is awesome", "arrays are fun", "i love coding", "practice makes perfect"]
// 2. flat array of all words
const allWords = sentences.flatMap((s) => s.trim().toLowerCase().split(" "));
console.log(allWords);
// ["hello", "world", "javascript", "is", "awesome", "arrays", "are", "fun", "i", "love", "coding", "practice", "makes", "perfect"]
// 3. word count
const wordCount = allWords.reduce((counts, word) => {
counts[word] = (counts[word] || 0) + 1;
return counts;
}, {});
console.log(wordCount);
// { hello: 1, world: 1, javascript: 1, is: 1, awesome: 1, ... }
// 4. longest sentence
const longest = sentences.reduce((max, s) =>
s.trim().length > max.trim().length ? s : max
);
console.log(longest.trim()); // "practice makes perfect"
// 5. sentences containing "is" or "are"
const filtered = sentences.filter((s) => {
const lower = s.trim().toLowerCase();
const words = lower.split(" ");
return words.includes("is") || words.includes("are");
});
console.log(filtered);
// ["JavaScript is awesome", " arrays ARE fun "]
תרגיל 4 - ניהול משימות - Todo List
const todos = [
{ id: 1, text: "Learn JavaScript", completed: true, priority: "high" },
{ id: 2, text: "Build a project", completed: false, priority: "high" },
{ id: 3, text: "Read documentation", completed: false, priority: "medium" },
{ id: 4, text: "Practice coding", completed: true, priority: "high" },
{ id: 5, text: "Watch tutorial", completed: false, priority: "low" },
{ id: 6, text: "Write tests", completed: false, priority: "medium" },
{ id: 7, text: "Deploy app", completed: true, priority: "high" },
{ id: 8, text: "Fix bugs", completed: false, priority: "high" }
];
// 1. incomplete todos
function getIncompleteTodos(todos) {
return todos.filter((todo) => !todo.completed);
}
console.log(getIncompleteTodos(todos));
// [Build a project, Read documentation, Watch tutorial, Write tests, Fix bugs]
// 2. high priority incomplete
function getHighPriorityIncomplete(todos) {
return todos.filter((todo) => !todo.completed && todo.priority === "high");
}
console.log(getHighPriorityIncomplete(todos));
// [Build a project, Fix bugs]
// 3. completion rate
function getCompletionRate(todos) {
const completed = todos.filter((todo) => todo.completed).length;
return (completed / todos.length) * 100;
}
console.log(getCompletionRate(todos)); // 37.5
// 4. group by priority
function getTodosByPriority(todos) {
return todos.reduce((groups, todo) => {
if (!groups[todo.priority]) groups[todo.priority] = [];
groups[todo.priority].push(todo);
return groups;
}, {});
}
console.log(getTodosByPriority(todos));
// alternative:
// Object.groupBy(todos, (todo) => todo.priority);
// 5. formatted list
function getFormattedList(todos) {
return todos.map((todo) => {
const checkbox = todo.completed ? "[x]" : "[ ]";
return `${checkbox} ${todo.text} (${todo.priority})`;
});
}
console.log(getFormattedList(todos));
// ["[x] Learn JavaScript (high)", "[ ] Build a project (high)", ...]
// 6. sort by priority without mutating original
function sortByPriority(todos) {
const priorityOrder = { high: 1, medium: 2, low: 3 };
return [...todos].sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
}
console.log(sortByPriority(todos));
תרגיל 5 - חנות מקוונת
const orders = [
{ id: 1, customer: "Alice", items: ["Laptop", "Mouse"], total: 5200, date: "2024-01-15" },
{ id: 2, customer: "Bob", items: ["Phone"], total: 3500, date: "2024-01-20" },
{ id: 3, customer: "Alice", items: ["Keyboard", "Monitor"], total: 2100, date: "2024-02-01" },
{ id: 4, customer: "Charlie", items: ["Tablet", "Case", "Charger"], total: 2800, date: "2024-02-10" },
{ id: 5, customer: "Bob", items: ["Headphones"], total: 350, date: "2024-02-15" },
{ id: 6, customer: "Alice", items: ["Webcam"], total: 250, date: "2024-03-01" },
{ id: 7, customer: "Charlie", items: ["Laptop"], total: 4999, date: "2024-03-05" }
];
// 1. total revenue
const totalRevenue = orders.reduce((sum, order) => sum + order.total, 0);
console.log(totalRevenue); // 19199
// 2. highest order
const highestOrder = orders.reduce((max, order) =>
order.total > max.total ? order : max
);
console.log(highestOrder); // { id: 1, customer: "Alice", total: 5200, ... }
// 3. unique customers
const uniqueCustomers = [...new Set(orders.map((order) => order.customer))];
console.log(uniqueCustomers); // ["Alice", "Bob", "Charlie"]
// 4. unique items sold (items is an array, so we need flatMap)
const uniqueItems = [...new Set(orders.flatMap((order) => order.items))];
console.log(uniqueItems);
// ["Laptop", "Mouse", "Phone", "Keyboard", "Monitor", "Tablet", "Case", "Charger", "Headphones", "Webcam"]
// 5. total spending per customer
const spendingPerCustomer = orders.reduce((totals, order) => {
totals[order.customer] = (totals[order.customer] || 0) + order.total;
return totals;
}, {});
console.log(spendingPerCustomer); // { Alice: 7550, Bob: 3850, Charlie: 7799 }
// 6. February orders sorted by total
const februaryOrders = orders
.filter((order) => order.date.startsWith("2024-02"))
.sort((a, b) => a.total - b.total);
console.log(februaryOrders);
// [Headphones(350), Keyboard+Monitor(2100), Tablet+Case+Charger(2800)]
// 7. customer with most orders
const orderCounts = orders.reduce((counts, order) => {
counts[order.customer] = (counts[order.customer] || 0) + 1;
return counts;
}, {});
const topCustomer = Object.entries(orderCounts)
.reduce((max, entry) => entry[1] > max[1] ? entry : max);
console.log(topCustomer[0]); // "Alice" (3 orders)
// 8. average order value
const avgOrder = totalRevenue / orders.length;
console.log(avgOrder); // 2742.71
תרגיל 6 - שטוח ומקובץ - flatMap
const classes = [
{
name: "Math 101",
students: [
{ name: "Alice", grade: 92 },
{ name: "Bob", grade: 78 },
{ name: "Charlie", grade: 85 }
]
},
{
name: "Science 201",
students: [
{ name: "Alice", grade: 88 },
{ name: "Diana", grade: 95 },
{ name: "Eve", grade: 72 }
]
},
{
name: "History 101",
students: [
{ name: "Bob", grade: 90 },
{ name: "Charlie", grade: 65 },
{ name: "Diana", grade: 82 }
]
}
];
// 1. flat array of all students with class name
const allStudents = classes.flatMap((cls) =>
cls.students.map((student) => ({
...student,
className: cls.name
}))
);
console.log(allStudents);
// [
// { name: "Alice", grade: 92, className: "Math 101" },
// { name: "Bob", grade: 78, className: "Math 101" },
// ...
// ]
// 2. unique student names
const uniqueStudents = [...new Set(allStudents.map((s) => s.name))];
console.log(uniqueStudents); // ["Alice", "Bob", "Charlie", "Diana", "Eve"]
// 3. average grade per student
const averages = uniqueStudents.map((name) => {
const studentGrades = allStudents.filter((s) => s.name === name);
const avg = studentGrades.reduce((sum, s) => sum + s.grade, 0) / studentGrades.length;
return { name, average: Math.round(avg * 100) / 100 };
});
console.log(averages);
// [
// { name: "Alice", average: 90 },
// { name: "Bob", average: 84 },
// { name: "Charlie", average: 75 },
// { name: "Diana", average: 88.5 },
// { name: "Eve", average: 72 }
// ]
// 4. class with highest average
const classAverages = classes.map((cls) => {
const avg = cls.students.reduce((sum, s) => sum + s.grade, 0) / cls.students.length;
return { name: cls.name, average: Math.round(avg * 100) / 100 };
});
const bestClass = classAverages.reduce((max, cls) =>
cls.average > max.average ? cls : max
);
console.log(bestClass); // { name: "Math 101", average: 85 }
תרגיל 7 - reduce מתקדם
// 1. flatten with reduce
function flatten(arr) {
return arr.reduce((flat, item) => flat.concat(item), []);
}
console.log(flatten([[1, 2], [3, 4], [5, 6]])); // [1, 2, 3, 4, 5, 6]
// 2. pipe with reduce
function pipe(...fns) {
return (value) => fns.reduce((result, fn) => fn(result), value);
}
const add10 = (x) => x + 10;
const multiply2 = (x) => x * 2;
const subtract5 = (x) => x - 5;
const transform = pipe(add10, multiply2, subtract5);
console.log(transform(5)); // ((5 + 10) * 2) - 5 = 25
// 3. groupBy with reduce
function groupBy(arr, keyFn) {
return arr.reduce((groups, item) => {
const key = keyFn(item);
if (!groups[key]) groups[key] = [];
groups[key].push(item);
return groups;
}, {});
}
const people = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 },
{ name: "Charlie", age: 25 },
{ name: "Diana", age: 30 }
];
console.log(groupBy(people, (p) => p.age));
// { 25: [{ name: "Alice", ... }, { name: "Charlie", ... }],
// 30: [{ name: "Bob", ... }, { name: "Diana", ... }] }
תרגיל 8 - אימות קלט - Validation
function validateForm(fields) {
// define validation rules
const validators = {
required: (value) => value.trim().length > 0 ? null : "Field is required",
email: (value) => value.includes("@") ? null : "Must be a valid email",
numeric: (value) => /^\d+$/.test(value) ? null : "Must contain only numbers",
minLength: (value, param) =>
value.length >= Number(param) ? null : `Must be at least ${param} characters`,
min: (value, param) =>
Number(value) >= Number(param) ? null : `Must be at least ${param}`
};
// validate each field
const results = fields.map((field) => {
const errors = field.rules
.map((rule) => {
const [ruleName, param] = rule.split(":");
return validators[ruleName](field.value, param);
})
.filter((error) => error !== null);
return { name: field.name, errors };
});
// build the result object
const errorFields = results.filter((r) => r.errors.length > 0);
const validFields = results
.filter((r) => r.errors.length === 0)
.map((r) => r.name);
const errors = errorFields.reduce((acc, field) => {
acc[field.name] = field.errors;
return acc;
}, {});
return {
isValid: errorFields.length === 0,
errors,
validFields
};
}
// test
const fields = [
{ name: "email", value: "test@email.com", rules: ["required", "email"] },
{ name: "password", value: "12", rules: ["required", "minLength:6"] },
{ name: "age", value: "25", rules: ["required", "numeric", "min:18"] },
{ name: "name", value: "", rules: ["required"] }
];
console.log(validateForm(fields));
// {
// isValid: false,
// errors: {
// password: ["Must be at least 6 characters"],
// name: ["Field is required"]
// },
// validFields: ["email", "age"]
// }