Learn JavaScript

lesson ১ / ৯ · object, destructuring, spread আর JSON

Module ৮ · object, destructuring, spread আর JSON

Object-এর ছবি: নাম দেওয়া box, reference, আর এক box-এর দুই নাম

Freeপড়া

এই lesson-এ যা শিখবে

  • Literal দিয়ে একটা object বানাও, আর dot ও bracket দিয়ে ওর key পড়ো, যোগ করো, বদলাও।
  • Variable-কে ভাবো একটা নাম হিসেবে, যেটা একটা object-কে দেখায়, তাই দুটো নাম একটাই box ভাগ করে নিতে পারে।
  • বুঝিয়ে বলো const থাকলেও ভেতরের object কেন বদলায়, আর spread-এর copy কেন একটা নতুন box।

Bob একটা cycling club-এর phone list সামলায়। Module 7-এ ও প্রতিটা member রাখত একটা array হিসেবে: ["Rafi", "01711", "Dhaka"]। এক সপ্তাহ ভালোই চলল। তারপর ও index 2-এ একটা email ঢুকিয়ে দিল, আর যত লাইন member[2] দিয়ে শহর পড়ত, সবাই email print করা শুরু করল। কোন নম্বরের মানে কী, কারো মনে থাকে না।

Bob নম্বর দিয়ে ঘর চায় না। ও চায় নাম দিয়ে ঘর: নাম, phone, শহর। Object ঠিক এটাই। এই module-এ object ঠিকঠাক শিখবে। সাথে শিখবে সেই একটা idea, যেটা object-এর বেশিরভাগ bug-এর ব্যাখ্যা দেয়: variable কোনো object ধরে রাখে না, শুধু একটা object-কে দেখায়।

Object মানে নাম দেওয়া কয়েকটা box

Object হলো এমন একটা value, যেটা অন্য value-দের নাম দিয়ে রাখে। প্রতিটা নাম একটা key, আর value-সহ একটা key হলো একটা property। Object লেখো curly brace দিয়ে। ভেতরে প্রতিটা property মানে key, একটা colon, তারপর value, আর property-গুলোর মাঝে comma। এটাই object literal।

const member = { name: "Rafi", phone: "01711", city: "Dhaka" };
console.log(member);
console.log(member.city);
console.log(Object.keys(member).length);
{ name: 'Rafi', phone: '01711', city: 'Dhaka' }
Dhaka
3
Run in Compiler

প্রথম লাইন পুরো object-টা print করে। দ্বিতীয়টা key দিয়ে একটা value পড়ে। তৃতীয়টা Object.keys দিয়ে key গোনে; Object.keys key-গুলোকে একটা array হিসেবে দেয়, Lesson 02-এ পুরোটা আছে।

Value যেকোনো কিছু হতে পারে: সংখ্যা, string, boolean, array, এমনকি আরেকটা object। Key-গুলো string। সাধারণ শব্দ হলে quote ছাড়াই লেখা যায়, ওপরের মতো।

Key পড়া: dot আর bracket

Property পড়ার দুটো উপায়। Dot দিয়ে, member.city, ছোট আর পরিষ্কার। Bracket দিয়ে, member["city"], যেটা একটা string নেয়, তাই key আসতে পারে কোনো variable বা input থেকে।

const member = { name: "Rafi", phone: "01711", city: "Dhaka" };
const field = "phone";
console.log(member.name);
console.log(member["city"]);
console.log(member[field]);
console.log(member.email);
Rafi
Dhaka
01711
undefined
Run in Compiler

শেষ লাইন এমন একটা key চেয়েছে যেটা নেই। JavaScript থামে না, দেয় undefined, ঠিক array-র শেষের পরে পড়ার মতো। Code লেখার সময়েই key জানা থাকলে dot নাও। Key যদি জানা যায় শুধু program চলার সময়, তাহলে bracket।

Key যোগ করা আর বদলানো

Object বাঁধা কিছু না। যে key এখনো নেই, তাতে assign করলে সেটা যোগ হয়। যে key আছে, তাতে assign করলে value বদলে যায়।

const member = { name: "Rafi" };
member.city = "Dhaka";
member["phone"] = "01711";
member.city = "Sylhet";
console.log(member);
console.log(Object.keys(member));
{ name: 'Rafi', city: 'Sylhet', phone: '01711' }
[ 'name', 'city', 'phone' ]
Run in Compiler

Key-গুলোর ক্রমটা দেখো। city যোগ হয়েছিল প্রথমে, বদলেছে পরে, তবু সে দ্বিতীয় জায়গাতেই আছে। বদলানো key নিজের জায়গা ছাড়ে না। নতুন key যায় শেষে। পুরো ক্রমের নিয়ম আছে Lesson 05-এ, আর সংখ্যার মতো দেখতে key-দের জন্য সেখানে একটা চমক আছে।

নাম একটা object-কে দেখায়

এই module-এর বাকিটা এই idea-র ওপর দাঁড়িয়ে। যে variable একটা object "ধরে রাখে", সে আসলে ধরে রাখে একটা reference: object-টা যেখানে থাকে, সেদিকে একটা তীর। Object নিজে হলো box। Variable হলো একটা নাম, যার হাতে ওই box-এর দিকে একটা তীর।

সংখ্যা আর string আলাদা। যে variable-এ 5 আছে, তার ভেতরে value-টাই আছে। Copy করলে 5-টাই copy হয়। কিন্তু object-কে দেখানো variable copy করলে copy হয় তীরটা, box না।

const rafi = { name: "Rafi", city: "Dhaka" };
const friend = rafi;
friend.city = "Sylhet";
console.log(rafi.city);
console.log(friend === rafi);
Sylhet
true
Run in Compiler

এখানে object মাত্র একটা। friend = rafi একই তীরকে একটা দ্বিতীয় নাম দিয়েছে। friend দিয়ে লেখাটা পড়ে ওই একটাই box-এ, তাই rafi দিয়ে পড়লেও সেটা দেখা যায়। দুটো object-এ === জিজ্ঞেস করে "এটা কি একই box?", আর এখানে উত্তর হ্যাঁ।

const তীরটা আটকায়, ভেতরটা না

Module 1-এ Maria এটা চালিয়ে অবাক হয়েছিল যে এটা চলে:

const list = [1, 2];
list.push(3);
console.log(list);
[ 1, 2, 3 ]
Run in Compiler

এখন ওকে উত্তর দিতে পারো। Array একটা object, তাই list-এর হাতে একটা reference। const কথা দেয় যে list নামটা চিরকাল একই তীর রাখবে। Box নিয়ে সে কোনো কথা দেয় না। push box-টা বদলায়, তাই const-এর আপত্তি করার কিছু নেই। const আপত্তি করে list = [9]-এ, যেটা নামটাকে একটা নতুন box-এর দিকে ঘোরাত।

প্রতিটা object-এর বেলাতেই এক কথা। const member = {} সারাদিন key পেতে পারে। Box-টাকেই জমিয়ে দিতে চাইলে আছে Object.freeze; Lesson 02 দেখাবে।

Copy মানে একটা নতুন box

আলাদা একটা দ্বিতীয় object চাইলে নতুন box বানাতেই হবে। সবচেয়ে সহজ উপায় spread: { ...rafi } একটা নতুন object বানায় আর rafi-র প্রতিটা property ওতে copy করে। ভেতরের ফাঁদটা দেখাবে Lesson 02: copy-টা শুধু এক স্তর গভীর।

নিচের widget-এ পাঁচটা লাইন দেখো, প্রতি লাইনে এক ধাপ। বাঁয়ে নামগুলো, ডানে box-গুলো, আর একটা তীর মানে একটা reference।

একই ধাপগুলো table-এ, প্রতি ধাপে একটা row।

ধাপLineChangedকী হলো
1const rafi = { name: "Rafi", city: "Dhaka" };rafi দেখায় box o1-কে, যার ভেতরে 2টা key।
2const friend = rafi;friend পায় একই তীর: friend === rafi হলো true।
3friend.city = "Sylhet";o1.cityলেখাটা পড়ে ওই একটাই box-এ, তাই rafi.city এখন Sylhet।
4const copy = { ...rafi };Spread একটা নতুন box o2 বানায়: copy === rafi হলো false।
5copy.city = "Khulna";o2.cityবদলায় শুধু নতুন box; rafi.city এখনো Sylhet।

আসল কথাটা ধাপ 2-এ: কোনো নতুন box আসে না, আসে শুধু একটা দ্বিতীয় তীর। ধাপ 4 উল্টো: পুরো একটা নতুন box, একই value দিয়ে ভরা। ধাপ 5-এর পরে দুটো box-এ দুটো আলাদা শহর।

এক নজরে object

const o = { key: value, other: value2 };   // an object literal
o.key          o["key"]       o[name]      // read by dot, by string, by a variable
o.newKey = v;                               // add a key, or replace its value
const p = o;                                // a second name for the same box
const c = { ...o };                         // a new box with the same properties
p === o                                     // true only for the same box
Example 1: input থেকে member card

Bob-এর input-এ একজন member-এর নাম, phone আর শহর, তিনটা শব্দ। এই program ওগুলো দিয়ে একটা object বানায় আর একটা card print করে। Key-গুলো আসে code থেকে, value-গুলো input থেকে।

const input = require("fs").readFileSync(0, "utf8");
const tokens = input.split(/\s+/).filter(Boolean);
const member = { name: tokens[0], phone: tokens[1], city: tokens[2] };
member.joined = 2026;

for (const key of Object.keys(member)) {
  console.log(key + ": " + member[key]);
}
console.log("fields " + Object.keys(member).length);
name: Nadia
phone: 01822
city: Rajshahi
joined: 2026
fields 4

Loop-টা প্রতিটা value bracket দিয়ে পড়ে, কারণ key একটা variable। member.key খুঁজত ঠিক "key" নামের একটা key। joined key-টা literal-এর পরে যোগ হয়েছে, তাই print হয় সবার শেষে।

Run in Compiler
Example 2: ভেতরে একই, box আলাদা

দুটো object দেখতে হুবহু এক হয়েও দুটো আলাদা box হতে পারে। এই program আগে reference মেলায়, তারপর ভেতরের জিনিস।

const a = { city: "Dhaka" };
const b = { city: "Dhaka" };
const c = a;
console.log(a === b);
console.log(a === c);
console.log(a.city === b.city);
c.city = "Comilla";
console.log(a.city, b.city);
false
true
true
Comilla Dhaka

a আর b দুটো literal দিয়ে বানানো, তাই দুটো box, আর === সেটাই বলে। c হলো a-র box-এর দ্বিতীয় নাম। ভেতরে কী আছে মেলাতে হলে key ধরে ধরে মেলাতে হয়; Lesson 06 এটাকে পুরো একটা deep equality বানাবে।

Run in Compiler

এটা কোথায় কাজে লাগে

  • GitHub REST API: প্রতিটা response, যেমন একটা repository বা একজন user, একটা JSON object, যেটা program পড়ে name আর id-এর মতো key-ওয়ালা JavaScript object হিসেবে।
  • npm: একটা project-এর package.json একটাই object, যার name, version, dependencies-এর মতো key Node আর npm সরাসরি পড়ে।
  • Node.js: process.env একটা object, যার key-গুলো environment variable, পড়া হয় process.env.PORT বা process.env[name] দিয়ে।

যে ভুলগুলো সবাই করে

১. যেখানে bracket লাগে, সেখানে dot

const prices = { tea: 20, coffee: 60 };
const item = "coffee";
console.log(prices.item);
console.log(prices[item]);
undefined
60

prices.item খোঁজে ঠিক "item" নামের একটা key, যেটা নেই, তাই print করে undefined। Dot কখনো variable পড়ে না। Key যদি variable-এ থাকে, লেখো prices[item]।

২. দুটো property-র মাঝে comma নেই

const member = { name: "Rafi" city: "Dhaka" };
console.log(member.city);

Node file-টা চালাতেই রাজি হয় না, বলে SyntaxError: Unexpected identifier 'city'। প্রথম value-র পরে parser একটা comma বা বন্ধ brace আশা করে। দুটো property আলাদা করতে space যথেষ্ট না।

৩. Assignment থেকে copy আশা করা

const original = { stock: 10 };
const backup = original;
original.stock = 0;
console.log(backup.stock);
0

Backup-এ 10 থাকার কথা ছিল, কিন্তু print হয় 0। backup = original তীরটা copy করেছে, তাই দুটো নামই একটা box দেখায়। আলাদা copy রাখতে চাইলে নতুন box বানাও: const backup = { ...original };।

মাথা খাটাও

তিনটা নাম, দুটো box

Zara কোণার case আগে পরখ করতে ভালোবাসে, তাই ও একটা ভাগের box-এর সাথে একটা দেখতে-একই box মিশিয়ে দেয়।

const a = { n: 1 };
const b = a;
const c = { n: 1 };
b.n = 2;
console.log(a.n + c.n);

এটা কী print করবে: 2, 3 না 4? সংখ্যা গোনার আগে box গোনো।

Exercise 1সহজ

Amara একটা chess club-এর member page দেখে। একজন member-এর profile কয়েকটা field নিয়ে: নাম, শহর, একটা motto, এরকম। প্রতিটা edit একটা field বসায়। কোনো field-এর প্রথম edit সেটা যোগ করে, আর পরে একই field-এর edit আগের value বদলে দেয়। সব edit-এর পরে profile-টা কেমন দাঁড়াল, Amara সেটা দেখতে চায়।

Input. প্রথম লাইনে n। পরের n টা লাইনের প্রতিটা একটা edit: field-এর নাম, একটা space, তারপর value। Value-র ভেতরে space থাকতে পারে।

Output. প্রথমে শেষ profile-এ কয়টা field আছে। তারপর প্রতিটা field-এর জন্য এক লাইন, field-গুলো প্রথম যে ক্রমে যোগ হয়েছিল সেই ক্রমে: field-এর নাম, একটা colon, একটা space, আর তার শেষ value।

Constraints. 1 <= n <= 1000; Each field name is 1 to 8 lowercase letters.; Each value is 1 to 30 letters, digits and spaces, and it never starts or ends with a space..

Sample.

5
name Rafi Hasan
city Sylhet
motto Think twice
city Dhaka
rating 1450
4
name: Rafi Hasan
city: Dhaka
motto: Think twice
rating: 1450
const input = require("fs").readFileSync(0, "utf8");
const lines = input.split("\n");
let at = 0;
const next = () => lines[at++];
const nextInt = () => Number(next());
const out = [];

// your code: read with next() and nextInt(), push every line of output to out

console.log(out.join("\n"));
Run in Compiler

profile-edits নামে grade হয়।

Exercise 2মাঝারি

Tanvir একটা ছোট guest house চালায়। প্রতিটা check-in একজন অতিথিকে একটা room-এ বসায়, আর একই room-এ পরের check-in আগের অতিথির জায়গা নেয়। রাতে ও জানতে চায় কয়েকটা room-এ কে আছে।

Input. n, তারপর n টা check-in, প্রতিটায় একটা room আর একজন অতিথির নাম। তারপর q, তারপর q টা room, যেগুলোর কথা জানতে চাওয়া হচ্ছে।

Output. প্রতিটা প্রশ্নের জন্য এক লাইন: ওই room-এর অতিথি, আর কেউ check-in না করলে vacant।

Constraints. 1 <= n, q <= 1000। Room আর নাম 1 থেকে 10টা অক্ষর বা digit।

Sample. নিচের input।

4
r101 Nadia
r102 Sumon
r101 Kenji
r205 Lima
3
r101
r103
r205
const input = require("fs").readFileSync(0, "utf8");
const tokens = input.split(/\s+/).filter(Boolean);
let at = 0;
const next = () => tokens[at++];
const nextInt = () => Number(next());
const out = [];

// your code: read with next() and nextInt(), push every line of output to out

console.log(out.join("\n"));

Sample-এর output.

Kenji
vacant
Lima
Run in Compiler
Exercise 3কঠিন

Reference নিয়ে নিজের ছবিটা ঠিক কি না, Maria যাচাই করতে চায়। ও চারটা command দিয়ে ছোট ছোট script লেখে। new x নাম x-কে একটা নতুন খালি box-এর দিকে ঘোরায়। alias y x y-কে সেই box-এর দিকে ঘোরায়, যেটা x দেখায়। set x k v x-এর box-এ key k বসায়, value v দিয়ে। get x k ওই key-এর value print করে, না থাকলে none।

Input. n, তারপর n টা command, প্রতি লাইনে একটা, ওপরের আকারে।

Output. প্রতিটা get command-এর জন্য এক লাইন।

Constraints. 1 <= n <= 1000। নাম, key আর value 1 থেকে 8টা ছোট হাতের অক্ষর বা digit। প্রতিটা নাম ব্যবহারের আগে new বা alias দিয়ে তৈরি হয়।

Sample. নিচের input।

7
new a
alias b a
set b city dhaka
new c
set c city khulna
get a city
get c city
const input = require("fs").readFileSync(0, "utf8");
const tokens = input.split(/\s+/).filter(Boolean);
let at = 0;
const next = () => tokens[at++];
const nextInt = () => Number(next());
const out = [];

// your code: read with next() and nextInt(), push every line of output to out

console.log(out.join("\n"));

Sample-এর output.

dhaka
khulna
Run in Compiler

সচরাচর যে প্রশ্নগুলো আসে

  • Array-ও কি একটা object?

    হ্যাঁ। Array হলো এমন একটা object, যার key-গুলো index, সাথে একটা বিশেষ length। এই কারণেই const array আর const object একই রকম আচরণ করে। Module 7-এর Lesson 05 খুঁটিনাটি দেখিয়েছে।

  • Key-এর চারপাশে কি quote লাগে?

    name বা city-এর মতো সাধারণ শব্দে লাগে না। Space বা dash-ওয়ালা key-তে লাগে, যেমন { "first name": "Rafi" }। এমন key পড়া যায় শুধু bracket দিয়ে।

  • console.log object-টা আমার literal-এর চেয়ে অন্যভাবে দেখায় কেন?

    Node object print করে নিজের পড়ার মতো একটা ঢঙে, brace-এর পরে space দিয়ে। এটা object-এর একটা ছবি, তুমি যে code লিখেছ সেটা না। Lesson 02 দেখাবে JSON.stringify, যেটা একটা বাঁধা text আকারে print করে।

  • কখন array-র বদলে object নেব?

    একই ধরনের জিনিসের ক্রম মেনে একটা list হলে array। নাম দেওয়া কয়েকটা অংশওয়ালা একটা জিনিস হলে object, যেমন নাম আর শহরওয়ালা একজন member। Lesson 04 এটাকে বাছাইয়ের পুরো একটা নিয়ম বানাবে।

মূল কথা

  • Object string key-এর নিচে value রাখে, লেখা হয় { key: value }।
  • Key জানা থাকলে পড়ো o.key দিয়ে, আর key variable-এ থাকলে o[name] দিয়ে।
  • নতুন key-তে assign করলে সেটা শেষে যোগ হয়; পুরোনো key-তে assign করলে value জায়গায় বসেই বদলায়।
  • Variable একটা object-এর reference রাখে; p = o একই box-কে একটা দ্বিতীয় নাম দেয়।
  • const reference-টা আটকায়, box না; { ...o } একটা নতুন box বানায়।
  • আরও গভীরে যেতে চাইলে: ভেতরের কলকব্জা, property-র ক্রম, hidden class, inline cache আর delete-এর খরচ (Pro)।

এরপর: object-এর প্রতিটা কাজ, একটা একটা করে, কী ফেরত দেয় আর object বদলায় কি না।

lesson ১ শেষ

শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।

পরেরটা: Object-এর প্রতিটা কাজ, একটা একটা করে: key, copy, destructuring আর JSON