2.7 אובייקטים פתרון
פתרון - אובייקטים
תרגיל 1 - כרטיס אישי
const person = {
name: "Alice",
age: 25,
city: "Tel Aviv",
hobbies: ["reading", "coding", "hiking"],
address: {
street: "Herzl 10",
city: "Tel Aviv",
zip: "6120001"
}
};
// 1. print name and city from address
console.log(person.name); // "Alice"
console.log(person.address.city); // "Tel Aviv"
// 2. add email
person.email = "alice@example.com";
// 3. add a hobby
person.hobbies.push("swimming");
// 4. change zip code
person.address.zip = "6130002";
// 5. delete age
delete person.age;
// 6. print all keys
console.log(Object.keys(person));
// ["name", "city", "hobbies", "address", "email"]
// 7. print all values
console.log(Object.values(person));
// 8. print each key-value pair
Object.entries(person).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
תרגיל 2 - מתודות של אובייקט
const bankAccount = {
owner: "Alice",
balance: 0,
history: [],
deposit(amount) {
this.balance += amount;
this.history.push({ type: "deposit", amount, date: new Date().toISOString() });
console.log(`Deposited ${amount}. New balance: ${this.balance}`);
},
withdraw(amount) {
if (amount > this.balance) {
console.log(`Withdrawal of ${amount} failed - insufficient funds. Balance: ${this.balance}`);
return false;
}
this.balance -= amount;
this.history.push({ type: "withdrawal", amount, date: new Date().toISOString() });
console.log(`Withdrew ${amount}. New balance: ${this.balance}`);
return true;
},
getBalance() {
return this.balance;
},
getHistory() {
return this.history;
},
printStatement() {
console.log(`\n=== Account Statement for ${this.owner} ===`);
this.history.forEach((entry) => {
const sign = entry.type === "deposit" ? "+" : "-";
console.log(`${entry.type}: ${sign}${entry.amount}`);
});
console.log(`Current balance: ${this.balance}`);
console.log("================================\n");
}
};
// test
bankAccount.deposit(1000); // Deposited 1000. New balance: 1000
bankAccount.deposit(500); // Deposited 500. New balance: 1500
bankAccount.withdraw(200); // Withdrew 200. New balance: 1300
bankAccount.withdraw(2000); // Withdrawal of 2000 failed - insufficient funds
bankAccount.printStatement();
// === Account Statement for Alice ===
// deposit: +1000
// deposit: +500
// withdrawal: -200
// Current balance: 1300
// ================================
תרגיל 3 - מיזוג ועדכון
const defaultSettings = {
theme: "light",
language: "en",
fontSize: 14,
notifications: true,
autoSave: true
};
const userSettings = {
theme: "dark",
language: "he",
fontSize: 16
};
// 1. merge with spread (user overrides defaults)
const mergedSettings = { ...defaultSettings, ...userSettings };
console.log(mergedSettings);
// { theme: "dark", language: "he", fontSize: 16, notifications: true, autoSave: true }
// 2. create a copy and change theme
const copy = { ...mergedSettings };
copy.theme = "blue";
console.log(mergedSettings.theme); // "dark" - not affected
console.log(copy.theme); // "blue"
// 3. add nested sidebar property
mergedSettings.sidebar = { width: 250, visible: true };
console.log(mergedSettings.sidebar); // { width: 250, visible: true }
// 4. freeze defaults
Object.freeze(defaultSettings);
defaultSettings.theme = "rainbow"; // does nothing
console.log(defaultSettings.theme); // "light" - unchanged
תרגיל 4 - ספר טלפונים
function addContact(phoneBook, name, phone) {
phoneBook[name] = phone;
console.log(`Added: ${name} -> ${phone}`);
}
function removeContact(phoneBook, name) {
if (name in phoneBook) {
delete phoneBook[name];
console.log(`Removed: ${name}`);
} else {
console.log(`Contact "${name}" not found`);
}
}
function findContact(phoneBook, name) {
if (name in phoneBook) {
return phoneBook[name];
}
return `Contact "${name}" not found`;
}
function updateContact(phoneBook, name, newPhone) {
if (name in phoneBook) {
phoneBook[name] = newPhone;
console.log(`Updated: ${name} -> ${newPhone}`);
} else {
console.log(`Contact "${name}" not found`);
}
}
function listContacts(phoneBook) {
const sorted = Object.entries(phoneBook).sort((a, b) =>
a[0].localeCompare(b[0])
);
console.log("=== Phone Book ===");
sorted.forEach(([name, phone]) => {
console.log(`${name}: ${phone}`);
});
}
function searchContacts(phoneBook, query) {
const lowerQuery = query.toLowerCase();
return Object.entries(phoneBook)
.filter(([name]) => name.toLowerCase().includes(lowerQuery))
.map(([name, phone]) => ({ name, phone }));
}
// test
const phoneBook = {};
addContact(phoneBook, "Alice", "050-1234567");
addContact(phoneBook, "Bob", "052-9876543");
addContact(phoneBook, "Alice Smith", "054-1112222");
addContact(phoneBook, "Charlie", "053-5555555");
console.log(findContact(phoneBook, "Bob")); // "052-9876543"
listContacts(phoneBook);
// === Phone Book ===
// Alice: 050-1234567
// Alice Smith: 054-1112222
// Bob: 052-9876543
// Charlie: 053-5555555
console.log(searchContacts(phoneBook, "alice"));
// [{ name: "Alice", phone: "050-1234567" }, { name: "Alice Smith", phone: "054-1112222" }]
updateContact(phoneBook, "Bob", "052-0000000");
removeContact(phoneBook, "Charlie");
listContacts(phoneBook);
תרגיל 5 - השוואת אובייקטים
function deepEqual(obj1, obj2) {
// handle exact same reference or both primitive and equal
if (obj1 === obj2) return true;
// handle null/undefined
if (obj1 === null || obj2 === null) return false;
if (obj1 === undefined || obj2 === undefined) return false;
// handle different types
if (typeof obj1 !== typeof obj2) return false;
// handle non-object types (primitives already compared above)
if (typeof obj1 !== "object") return false;
// handle arrays
if (Array.isArray(obj1) !== Array.isArray(obj2)) return false;
if (Array.isArray(obj1)) {
if (obj1.length !== obj2.length) return false;
return obj1.every((item, index) => deepEqual(item, obj2[index]));
}
// handle objects
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
return keys1.every((key) => deepEqual(obj1[key], obj2[key]));
}
// tests
console.log(deepEqual({ a: 1, b: 2 }, { a: 1, b: 2 })); // true
console.log(deepEqual({ a: 1, b: 2 }, { a: 1, b: 3 })); // false
console.log(deepEqual({ a: { b: 1 } }, { a: { b: 1 } })); // true
console.log(deepEqual({ a: [1, 2, 3] }, { a: [1, 2, 3] })); // true
console.log(deepEqual({ a: [1, 2, 3] }, { a: [1, 2, 4] })); // false
console.log(deepEqual(null, null)); // true
console.log(deepEqual({ a: 1 }, { a: 1, b: 2 })); // false
תרגיל 6 - Optional Chaining
const apiResponse = {
status: 200,
data: {
user: {
id: 1,
name: "Alice",
profile: {
avatar: "alice.jpg",
bio: "Developer"
},
posts: [
{ id: 101, title: "Hello World", comments: [{ text: "Nice!" }] },
{ id: 102, title: "JavaScript Tips", comments: [] }
]
}
}
};
// 1. user name
console.log(apiResponse.data?.user?.name);
// "Alice"
// 2. user avatar
console.log(apiResponse.data?.user?.profile?.avatar);
// "alice.jpg"
// 3. first comment of first post
console.log(apiResponse.data?.user?.posts?.[0]?.comments?.[0]?.text);
// "Nice!"
// 4. title of third post (doesn't exist)
console.log(apiResponse.data?.user?.posts?.[2]?.title);
// undefined
// 5. user phone (doesn't exist)
console.log(apiResponse.data?.user?.phone);
// undefined
// 6. user city (doesn't exist)
console.log(apiResponse.data?.user?.profile?.address?.city);
// undefined
תרגיל 7 - מערכת ניהול מלאי
const inventory = {};
function addProduct(inventory, name, price, quantity) {
inventory[name] = { price, quantity };
console.log(`Added: ${name} (price: ${price}, qty: ${quantity})`);
}
function sellProduct(inventory, name, quantity) {
if (!(name in inventory)) {
return `Product "${name}" not found`;
}
if (inventory[name].quantity < quantity) {
return `Not enough stock for "${name}". Available: ${inventory[name].quantity}`;
}
inventory[name].quantity -= quantity;
const total = inventory[name].price * quantity;
console.log(`Sold ${quantity}x ${name} for ${total}`);
return total;
}
function restock(inventory, name, quantity) {
if (!(name in inventory)) {
console.log(`Product "${name}" not found`);
return;
}
inventory[name].quantity += quantity;
console.log(`Restocked ${name}: +${quantity} (now: ${inventory[name].quantity})`);
}
function getReport(inventory) {
const entries = Object.entries(inventory);
return {
totalProducts: entries.length,
totalItems: entries.reduce((sum, [, p]) => sum + p.quantity, 0),
totalValue: entries.reduce((sum, [, p]) => sum + p.price * p.quantity, 0),
outOfStock: entries
.filter(([, p]) => p.quantity === 0)
.map(([name]) => name),
lowStock: entries
.filter(([, p]) => p.quantity > 0 && p.quantity < 5)
.map(([name]) => name)
};
}
function getMostExpensive(inventory) {
const entries = Object.entries(inventory);
if (entries.length === 0) return null;
const [name, product] = entries.reduce((max, current) =>
current[1].price > max[1].price ? current : max
);
return { name, ...product };
}
function search(inventory, query) {
const lowerQuery = query.toLowerCase();
return Object.entries(inventory)
.filter(([name]) => name.toLowerCase().includes(lowerQuery))
.map(([name, product]) => ({ name, ...product }));
}
// test
addProduct(inventory, "Laptop", 5000, 10);
addProduct(inventory, "Mouse", 100, 50);
addProduct(inventory, "Keyboard", 250, 3);
addProduct(inventory, "Monitor", 1500, 8);
addProduct(inventory, "Headphones", 350, 0);
sellProduct(inventory, "Mouse", 50); // sells all mice
sellProduct(inventory, "Laptop", 2);
console.log(getReport(inventory));
// {
// totalProducts: 5,
// totalItems: 69,
// totalValue: 43750,
// outOfStock: ["Mouse", "Headphones"],
// lowStock: ["Keyboard"]
// }
console.log(getMostExpensive(inventory));
// { name: "Laptop", price: 5000, quantity: 8 }
console.log(search(inventory, "key"));
// [{ name: "Keyboard", price: 250, quantity: 3 }]
תרגיל 8 - טרנספורמציות אובייקטים
// 1. invertObject
function invertObject(obj) {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => [value, key])
);
}
console.log(invertObject({ a: 1, b: 2, c: 3 }));
// { 1: "a", 2: "b", 3: "c" }
// 2. pick
function pick(obj, keys) {
return Object.fromEntries(
Object.entries(obj).filter(([key]) => keys.includes(key))
);
}
console.log(pick({ a: 1, b: 2, c: 3, d: 4 }, ["a", "c"]));
// { a: 1, c: 3 }
// 3. omit
function omit(obj, keys) {
return Object.fromEntries(
Object.entries(obj).filter(([key]) => !keys.includes(key))
);
}
console.log(omit({ a: 1, b: 2, c: 3, d: 4 }, ["b", "d"]));
// { a: 1, c: 3 }
// 4. mapValues
function mapValues(obj, fn) {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => [key, fn(value)])
);
}
console.log(mapValues({ a: 1, b: 2, c: 3 }, (val) => val * 2));
// { a: 2, b: 4, c: 6 }
// 5. mapKeys
function mapKeys(obj, fn) {
return Object.fromEntries(
Object.entries(obj).map(([key, value]) => [fn(key), value])
);
}
console.log(mapKeys({ name: "Alice", age: 25 }, (key) => key.toUpperCase()));
// { NAME: "Alice", AGE: 25 }