לדלג לתוכן

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"]
// }