Skip to content

Chapter 13: JavaScript Array Methods

13-1: map কইরা দেন ভাই

ধর, তোর কাছে একটা অ্যারের মধ্যে অনেকগুলা সংখ্যা আছে। তোর কাজ হবে, সবগুলা সংখ্যারে দ্বিগুণ করবি, সেই দ্বিগুণ মানগুলা একটা অ্যারের মধ্যে রেখে রিটার্ন করবি।

এই কাজ করার জন্য তুই একটা ফাংশন লিখবি। সেখানে প্যারামিটার হিসেবে একটা অ্যারে নিবি। আর ফাংশনের ভিতরে দ্বিগুণ মানগুলো রাখার জন্য একটা অ্যারে ডিক্লেয়ার করবি। তারপর লুপ চালাবি। লুপের ভিতরে মানগুলো দ্বিগুণ করবি। এরপর দ্বিগুণ মান সেই অ্যারের মধ্যে পুশ করবি। এরপর লুপ শেষ হলে ডাবল সংখ্যাওয়ালা অ্যারেটা রিটার্ন করে দিবি।

javascript
function doubleAll(numbers) {
  const doubled = [];
  for (const num of numbers) {
    const value = num * 2;
    doubled.push(value);
  }
  return doubled;
}

const numbers = [4, 5, 2, 8, 10];
console.log(doubleAll(numbers));

Output: [8, 10, 4, 16, 20];

মজার বিষয় হচ্ছে, এত বড় কাজ জাস্ট এক লাইনে প্যাকেট করে দিতে পারবি।

কিছুই না, জাস্ট অ্যারের নামের পর ডট চিহ্ন (.) দিয়ে map লিখবি। সেটার ভিতরে একটা arrow ফাংশন লিখবি। arrow ফাংশনের কাজ হবে, একটা উপাদান নিবি, আর উপাদানরে ডাবল করবি।

তাহলেই যে অ্যারের ওপরে map চালাইছিস, তার সবগুলা উপাদান ডাবল হইয়া সেই ডাবল উপাদানগুলা তোকে রিটার্ন করে দিবে। কী মজা!

javascript
const numbers = [4, 5, 2, 8, 10];
const result = numbers.map((num) => num * 2);
console.log(result);

Output: [8, 10, 4, 16, 20];

শুধু ডাবল না; বরং এই টাইপের আরও অনেক কাজ তুই করতে পারবি। যেমন, সব উপাদানের সাথে 5 যোগ করবি। এক লাইনের কোড দিয়ে বা সব উপাদানকে ২ দিয়ে ভাগ করে অর্ধেক করে ফেলতে পারবি।

javascript
const numbers = [12, 10, 8, 15, 7];
const fiveBonus = numbers.map((num) => num + 5);
const halves = numbers.map((num) => num / 2);
console.log(fiveBonus);
console.log(halves);

Output: [17, 15, 13, 20, 12][(6, 5, 4, 7.5, 3.5)];

আবার স্ট্রিং মানওয়ালা অ্যারের মধ্যেও map চালাইতে পারবি; যাতে সবগুলা উপাদানের মধ্যে কয়টা ক্যারেক্টার আছে। অর্থাৎ স্ট্রিংগুলার লেংথ জানতে পারবি। আবার সেইম স্টাইলে প্রত্যেকটা স্ট্রিংয়ের প্রথম উপাদানকে নিয়ে একটা অ্যারেতে নিয়ে আসতে পারবি।

javascript
const friends = ["Tom", "Jhon", "Micheal", "Oliver"];
const lengths = friends.map((frnd) => frnd.length);
const firstLetters = friends.map((friend) => friend[0]);
console.log(lengths);
console.log(firstLetters);

Output: [3, 4, 7, 6][("T", "J", "M", "O")];

map() খুবই শক্তিশালী একটা ফাংশন, যা অ্যারের প্রতিটা উপাদানের ওপর নির্দিষ্ট কাজ করে এবং একটা নতুন অ্যারে রিটার্ন করে। একটু প্র্যাকটিস করলে তুই এটা দিয়া অনেক কাজ সহজে করতে পারবি।

forEach কী?

ধর, আমরা একটা number-এর অ্যারে ডিক্লেয়ার করলাম।

javascript
const numbers = [1, 5, 6, 4, 15];

এখন, এই জায়গায় যদি আমরা map ব্যবহার করতাম, তাহলে সে প্রতিটা উপাদানের ওপর গিয়া কিছু একটা কাজ করত এবং রেজাল্টটা return করত। আর forEach কী করে? forEach প্রত্যেকটা উপাদানের ওপর কাজ করে, কিন্তু কিছু return করে না।

সোজা কথায়, map প্রতিটা উপাদানের ওপর গিয়া কাজ করে এবং সেটারে return করে দেয়। কিন্তু forEach কাজ করলেও কিছু return করে না। এটাই তাদের মধ্যে পার্থক্য।

javascript
const numbers = [1, 5, 6, 4, 15];
const result = numbers.forEach((n) => console.log(n));
console.log(result);

Output: 1;
5;
6;
4;
15;
undefined;

এই কোডটা খেয়াল কর, আমরা আমাদের অ্যারের ওপর forEach করছি এবং প্রতিটা উপাদানকে console-এ দেখতে পাচ্ছি। কিন্তু যখন আমরা result ভ্যারিয়েবলটা আউটপুটে দেখতে চাইছি, তখন undefined দেখাচ্ছে। কারণ, forEach কিছু return করে না।

সুতরাং, map এবং forEach কাছাকাছি জিনিস, কিন্তু পার্থক্য হইল map রেজাল্টকে অ্যারেতে return করবে, আর forEach করবে না।

Filter:

এখন filter দিয়ে কী করবি? filter দিয়া কিছু জিনিস বাছাই করবি। অনেকটা ছাকনির মতো। কিছু জিনিস ছাকনিতে আটকে যায়। যেমন, চা পাতা আটকে যায়, আর চা নিচে পড়ে যায়। এ রকম কিছু জিনিস বের করার ছাকনিই হচ্ছে filter মেথড। ধর, আমাদের কাছে কিছু খেলোয়াড়ের উচ্চতার অ্যারে আছে। আমরা 70 ইঞ্চির ওপরে যারা আছে, তাদেরকে বাস্কেটবল খেলার জন্য বাছাই করতে চাই।

javascript
const players = [75, 65, 67, 71, 55, 59];
const selected = players.filter((p) => p > 70);

console.log(selected);

Output: [75, 71];

এখানে খেয়াল কর, আমরা আমাদের শর্ত অনুযায়ী যাদের চেয়েছিলাম, তাদেরই একটা অ্যারে return করা হইছে। শর্ত যদি 80-এর ওপরে দিস, তখন দেখবি filter আমাদের empty অ্যারে return করবে। কারণ, একজনের উচ্চতাও 80-এর ওপরে নাই।

তুই চাইলে কোনো string-এর থেকেও filter করে কিছু বাছাই করতে পারিস।

javascript
const friends = ["tom", "john", "micheal", "oliver", "tim", "joshna"];
const oldFriends = friends.filter((friend) => friend.length > 4);
console.log(oldFriends);

Output: ["micheal", "oliver", "joshna"];

এখানে আমরা যেসব বন্ধুর নামের লেংথ 4-এর বেশি, তাদের oldFriends-এ স্টোর করছি।

Find

এখন, filter-এর কাছাকাছি আরেকটা জিনিস আছে, সেটা হইলো find। find হলো filter-এর মতই, কিন্তু পার্থক্য হইলো, filter শর্ত মিলে গেলে যেগুলা মিলবে, তাদের সবগুলাকে অ্যারে হিসেবে return করে, আর find ম্যাচ করলে শুধু প্রথম উপাদানকে return করে।

আবার filter যদি শর্ত পূরণ করে এমন কোনো উপাদান না পায়, তাহলে রিটার্ন হিসেবে একটা খালি অ্যারে ( [ ] ) রিটার্ন করবে। অন্যদিকে find যদি কোনো ম্যাচিং উপাদান না পায়, যেটা শর্ত পূরণ করবে, তাহলে সে undefined রিটার্ন করবে।

javascript
const players = [75, 65, 67, 60, 71, 55, 59];
const selected = players.find((player) => player > 70);
console.log(selected);

Output: 75;

filter সব সময় অ্যারে return করবে, আর find প্রথম উপাদান return করবে, যা শর্ত পূরণ করে। যদি শর্ত পূরণ না করে, find তোকে undefined রিটার্ন করবে।

FindIndex

ম্যাচিং কোনো উপাদান পেলে find উপাদানের মান দিয়ে দেয়। আর findIndex দিয়ে দিবে প্রথম যে উপাদান শর্ত পূরণ করবে, তার index-এর মান। যদি পুরা অ্যারের কোনো উপাদানও শর্ত পূরণ না করে, তাহলে রিটার্ন করবে -1, কারণ -1 ইনডেক্স মানে সেই উপাদানটা অ্যারের মধ্যে নাই।

javascript
const numbers = [10, 25, 30, 50];
const index = numbers.findIndex((num) => num > 27);
console.log(index);

Output: 2;

const veryBig = numbers.findIndex((num) => num > 100);
console.log(veryBig);

Output: -1;

একটা না তিনটা প্যারা

এতক্ষণ পর্যন্ত আমি map, filter, find, forEach ইত্যাদির মধ্যে যে অ্যারো ফাংশন দিচ্ছিলাম, সেখানে একটা প্যারামিটার ছিল। কারণ, বেশির ভাগ সময় একটা প্যারামিটারের দরকার পড়ে। তবে ভিতরে ভিতরে কিন্তু তিনটা প্যারামিটার। এই তিনটা প্যারামিটারের সবগুলা বেশির ভাগ সময় কাজে লাগে না। তারপরেও জেনে রাখলি। ক্ষতি কী?

আমি যদি তিনটা প্যারামিটারের কথা বলি, তাহলে প্রথমটা হচ্ছে, যেটা এতক্ষণ ধরে ইউজ করতেছিলাম, সেটাকে বলে element (অ্যারের উপাদান। আমরা আমাদের সুবিধামতো মিল আছে, এমন একটা নাম দেই), তারপর আছে index অর্থাৎ যে যে ইলিমেন্ট আসতেছে, অ্যারের মধ্যে সেটার ইনডেক্স কত। আর থার্ড প্যারামিটার হচ্ছে পুরা অ্যারেটা দিয়ে দিবে।

javascript
const students = [
  { name: "John", age: 20 },
  { name: "Adam", age: 22 },
  { name: "Tom", age: 19 },
  { name: "Lucy", age: 21 },
];

const studentInfo = students.map((student, index, array) => {
  return `${student.name}, ${index + 1} of ${array.length} students.`;
});
console.log(studentInfo);

Output: [
  "John, 1 of 4 students.",
  "Adam, 2 of 4 students.",
  "Tom, 3 of 4 students.",
  "Lucy, 4 of 4 students.",
];

ওপরে আমরা map-এর ভিতরে তিনটা প্যারামিটারই ইউজ করতেছি। index-এর সাথে 1 যোগ করে পজিশন বের করতেছি। আবার জাস্ট দেখার জন্য পুরা অ্যারের লেংথ প্রত্যেক লাইনে ইউজ করতেছি।

Summary:

আমরা একটা অ্যারের ওপর map চালাই, তাহলে প্রত্যেকটা উপাদানের ওপর কিছু একটা অপারেশন চালাবে এবং সেই অপারেশনের রেজাল্ট দিয়া একটা নতুন অ্যারে তৈরি করে সেই অ্যারেকে return করে দিবে।

আর যদি forEach করি, তাহলে প্রত্যেকটা উপাদানের ওপর কিছু একটা অপারেশন করবি, কিন্তু কিছুই return করবি না, সেখানেই শেষ।

Filter করলে একটা শর্ত দিবি, যেই উপাদানগুলো শর্ত পূরণ করবে, তাদেরকে একটা অ্যারেতে return করবে। আর যদি শর্ত পূরণ করে, এমন কোনো উপাদান না পায়, তাহলে রিটার্ন হিসেবে একটা খালি অ্যারে ( [ ] ) রিটার্ন করবে।

Find করলে একটা শর্ত দিবি, আর যে উপাদানটা প্রথমে শর্ত পূরণ করবে, তাকেই return করবে। আর যদি কোনো ম্যাচিং উপাদান না পায়, যেটা শর্ত পূরণ করবে, তাহলে সে undefined রিটার্ন করবে।

Practice:

  1. একটা স্ন্যাক্সের দোকানে বিভিন্ন আইটেম আছে, যেগুলার দাম এভাবে আছে: [30, 45, 20, 60, 10]। তুই সবগুলা আইটেমের দাম 13 টাকা করে বাড়াইতে চাস। এই কাজটা map দিয়ে করে দেখ।
  2. ধর, তোর একটা নামের লিস্ট আছে [ 'Messi', 'Maradona', 'Pele', 'Zidane', 'Ronaldo']। তুই চাস, লিস্ট থেকে 5 অক্ষরের বেশি যাদের নাম, শুধু তাদেরকে একটা আলাদা লিস্টে রাখবি। filter দিয়ে করে দেখ কীভাবে হবে।
  3. তোর একটা number লিস্ট আছে [10, 15, 20, 25, 30, 35]। তুই এমন একটা প্রোগ্রাম লিখে দেখ, যা 20-এর ওপরে প্রথম যে সংখ্যাটা আছে, সেটা খুঁজে বের করে দিবে।
  4. তোর কিছু হাইটের ডেটা আছে, যেমন: [65, 70, 68, 72, 68, 73]। 69 ইঞ্চির বেশি লম্বা যাদের হাইট আছে, তাদের শুধু খুঁজে বের কর filter দিয়ে।
  5. তোর কাছে কিছু সংখ্যা আছে: [7, 10, 15, 20, 25, 30]। তুই চাস প্রত্যেক সংখ্যা 3 দিয়ে ভাগ করলে কত হয়, সেটা এক এক করে দেখতে। map ইউজ করে প্রোগ্রাম লিখ।
  6. তোর বন্ধুদের নামের লিস্ট আবারও আছে ['Leonardo', 'Brad Pitt', 'Kate Winslet', 'Audrey Hepburn', 'Johnny Depp']। থার্ড অক্ষর বের করতে চাস প্রত্যেক বন্ধুর নামে। map দিয়ে কাজটা করে ফেল।
  7. ধর, তুই একটা নামের array নিয়া আছিস: ['Tom', 'Harry', 'Sam', 'Jack']। এখন এমন নাম খুঁজে বের কর, যা 'H' দিয়া শুরু হয়। find ইউজ কর।
  8. তুই একটা array [1, 2, 3, 4, 5] নিয়া বসে আছিস। তুই একটা ফাংশন লিখ, যা forEach ইউজ করে প্রতিটা সংখ্যা console-এ প্রিন্ট করে।
  9. ['cow', 'goat', 'sheep', 'horse'] এই অ্যারে থেকে প্রতিটা নাম forEach দিয়ে console-এ প্রিন্ট কর।

13-2: every লিটনের some flat

জাভাস্ক্রিপ্টে some() আর every() হলো অ্যারে মেথড, যেগুলো নির্দিষ্ট শর্তের ওপর ভিত্তি করে অ্যারের সব আইটেমকে চেক করে। কিন্তু এদের কাজ একটু আলাদা। চল, এদের কাজ আর ব্যবহার সম্পর্কে জেনে নিই!

some() মেথড

some একটা ইংরেজি শব্দ। এইটার মানে হচ্ছে কিছু। অর্থাৎ অন্তত একটা বা তার বেশি। এইটুক যদি বুঝছ, তাহলে আর প্যারা নাই। জাভাস্ক্রিপ্টে অ্যারের নামের পর ডট দিয়ে তারপর some লিখে সেটার ভিতরে filter বা find-এর মতো করে তুই একটা শর্তওয়ালা অ্যারো ফাংশন দিয়ে দিতে পারস। যদি অ্যারের মধ্যে অন্তত একটা আইটেম বা উপাদান ঐ শর্ত পূরণ করে, তাহলে এটা true রিটার্ন করে। আর যদি কোনো আইটেমই শর্ত পূরণ না করে, তাহলে false রিটার্ন করে।

যেমন: ধর, তুই দেখতে চাস যে, কোনো একজন স্টুডেন্ট পাস করেছে কি না।

javascript
const scores = [30, 45, 55, 80, 90];
const hasPassed = scores.some((score) => score >= 50);
console.log(hasPassed);

Output: true;

এখানে some() চেক করছে যে, অন্তত একটা আইটেম (স্কোর) 50 বা তার বেশি আছে কি না। যেহেতু 80 এবং 90 পয়েন্ট 50-এর বেশি, তাই some তোকে true রিটার্ন করেছে।

every() মেথড

every() মেথড চেক করে, অ্যারের সব আইটেম শর্ত পূরণ করে, তাহলে এটা true রিটার্ন করে। আর যদি একটা আইটেমও শর্ত পূরণ না করে, তাহলে এটা false রিটার্ন করে।

যেমন: ধর, তুই দেখতে চাস, সব স্টুডেন্ট পাস করেছে কি না।

javascript
const scores = [60, 70, 55, 80, 90];
const allPassed = scores.every((score) => score >= 50);
console.log(allPassed);

Output: true;

এখানে every() চেক করেছে যে, সব স্কোরই 50 বা তার বেশি কি না। যেহেতু সব স্কোরই 50 বা তার বেশি, তাই every() true রিটার্ন করেছে।

আরেকটা উদাহরণ, যেখানে every() false রিটার্ন করবে—

javascript
const scores = [60, 45, 55, 80, 90];
const allPassed = scores.every((score) => score >= 50);
console.log(allPassed);

Output: false;

এখানে 45 উপাদান 50-এর নিচে, তাই every তোকে false রিটার্ন করেছে।

flat

নেস্টেড অ্যারে বলতে বুঝায় একটা অ্যারের উপাদানও আরেকটা অ্যারে। সেটার ভিতরেও আরেকটা অ্যারে থাকতে পারে। যদি অনেক লেভেলে নেস্টেড অ্যারে থাকে, তাহলে অ্যারের নাম, এরপর ডট দিয়ে flat লিখে কল করে দিবি। যদি অনেক অনেক মাল্টি লেভেলের নেস্টেড অ্যারে হয়, তখন একটা প্যারামিটার দিয়ে দিবি। যেমন, আমার দুই লেভেল ডেপ্থ অ্যারে আছে, তাই আমি 2 প্যারামিটার হিসেবে দিয়ে দিছি।

ধর, তোর কাছে nested array আছে এবং তুই সেটাকে একটি single array-তে flatten করতে চাস। flat এই কাজটা করে।

javascript
const nested = [1, 2, [3, 4, [5, 6]]];
const flattened = nested.flat(2);
console.log(flattened);

Output: [1, 2, 3, 4, 5, 6];

Summary

some চেক করে, অ্যারের অন্তত একটা আইটেম শর্ত পূরণ করে কি না। যদি করে, তাহলে true।

every চেক করে, অ্যারের সব আইটেম শর্ত পূরণ করে কিনা। যদি করে, তাহলে true; না করলে false।

এই মেথডগুলো তুই সহজেই অ্যারে চেক করার জন্য ব্যবহার করতে পারবি। বিশেষ করে যখন অনেক আইটেমের মধ্যে নির্দিষ্ট শর্ত মিলিয়ে দেখতে হয়!

Practice:

  1. একটা অ্যারে বানা, যেটার নাম numbers এবং দেখা, এই অ্যারেতে 100-এর বড় কোনো সংখ্যা আছে কি না।
  2. একটা অ্যারে বানা, যেখানে সব এলিমেন্টের মান 5 দিয়ে ভাগ যায় কি না, তা চেক করার জন্য every মেথড ব্যবহার কর।
  3. words নামের একটা অ্যারে তৈরি কর এবং চেক কর, অন্তত একটি শব্দ "hello"-এর সমান কি না।
  4. ages নামের একটা অ্যারে তৈরি কর এবং দেখা, সবার বয়স 18-এর বেশি কি না।

13-3: Reduce করে একটা করে দাও (advanced)

Reduce

এই map, forEach, filter, find-এর ক্যাটাগরিতে হালকা ভেজাইল্লা জিনিস হচ্ছে reduce। রিডিউস মানে কমানো। এইখানে রিডিউস বলতে বুঝায় একটা অ্যারের অনেকগুলা উপাদানকে কমিয়ে কমিয়ে একটা জিনিসে বা একটা মানে নিয়ে আসার সিস্টেম। এটাতে একটু ভেজাল আছে। তাই আমি নিজেও মাঝেমধ্যে গুলিয়ে ফেলি। আর গুলিয়ে ফেললে আবার খুঁজে বের করে দেখার চেষ্টা করি। আসলে মাঝেমধ্যে দুইটা-একটা হালকা ভেজাইল্লা জিনিস দেখার দরকার আছে। তাহলে শরীরে একটা ঝাঁকি পড়ে।

রিডিউস দেখার আগে সিম্পল একটা কোড দেখে আসি।

আমার কাছে কয়েকটা সংখ্যার একটা array আছে। এইটার সব সংখ্যার যোগফল বের করার কাজ আমরা খুব সহজেই বের করে ফেলতে পারি।

javascript
const numbers = [4, 5, 7, 1, 2, 66];
let sum = 0;
for (const num of numbers) {
  sum = sum + num;
}
console.log(sum);

Output: 85;

এই কাজটাই reduce করলে এক লাইনে করা যায়। তবে এক লাইনে করতেই হবে। আর না করলে তুই প্রোগ্রামার হতে পারবি না, সেটা কিন্তু না। মেইন বিষয় হচ্ছে, তুই যদি শুরুর দিকে কোড করতে গিয়ে কয়েক লাইন বেশি কোড লিখে ফেলস, সেটা শুরুর দিকে বেশি দোষের না; বরং করতে করতে একসময় তুই নিজেও কিন্তু একসময় পেকে যাবি। তখন এইটাও একদিন তোর কাজে ইজি-পিজি, লেমন-চিজি মনে হবে। জাস্ট ধৈর্য ধরে চালিয়ে যেতে হবে। আর কিছু না।

তবে reduce দেখার যেহেতু নিয়ত আছে, তাই আগে একটু syntax দেখে বুঝার চেষ্টা কর। হাই লেভেলে দেখলে দুইটা জিনিস লাগবে।

javascript
arrayName.reduce(callbackFunction, initialValue);

তারমানে একটা array-এর নামের পর ডট চিহ্ন দিয়ে reduce লিখবি, তারপর দুইটা জিনিস লাগবে। একটা কলব্যাক ফাংশন, আরেকটা হচ্ছে প্রাথমিক মান বা ইনিশিয়াল ভ্যালু। কলব্যাক ফাংশন জিনিসটা আমি একটু পরে আরো বিস্তারিত ডিসকাস করতেছি।

ওপরের কোডের দিকে তাকালে দেখতে পাবি। ওপরে sum নামে একটা ভেরিয়েবলের প্রাথমিক মান বা ভ্যালু দেয়া আছে, সেটাই এইখানে initialValue হিসেবে হবে। ওপরে যেমন 0 দেয়া আছে। এইখানেও সেইম কাজ করতে গেলে 0 প্রাথমিক মান হিসেবে আসবে। অন্য কিছু করতে গেলে সেই অনুসারে প্রাথমিক মান আসবে।

আর callBack ফাংশনটা অনেকটা map-এর ভিতরে ফাংশনের কাছাকাছি। এখন ওপরের কোডে for of লুপ এবং লুপের ভিতরের কাজগুলা দেখ।

javascript
for (const num of numbers) {
  sum = sum + num;
}

এই কোডের মধ্যে কী হচ্ছে? এইখানে একটা লুপ চলতেছে, আর লুপ চলার জন্য একটা একটা করে উপাদান আসতেছে। তারপর আগের একটা sum ডিক্লেয়ার করা ছিল, সেটার মধ্যে লুপ ভ্যারিয়েবল num-এর যোগ করতেছে। কাজ কিন্তু এইটাই। এইটাকে একটু গুছিয়ে বললে সাধারণভাবে বলা যায়।

javascript
(accumulator, currentValue) => doSomeWork;

বা আমাদের উদাহরণের সাথে মিলিয়ে বললে বলা যায়—

javascript
(accumulator, currentValue) => accumulator + currentValue;

অর্থাৎ দুইটা প্যারামিটারওয়ালা একটা অ্যারো ফাংশন আসবে। সেটার মধ্যে আগের মান প্রথম প্যারামিটার হিসেবে থাকবে (sum-এর কথা চিন্তা করতে পারস), তারপর সেকেন্ড প্যারামিটার হবে লুপ চললে প্রত্যেকবার নতুন যে উপাদান আসবে (num-এর কথা চিন্তা করতে পারস) সেটা। তারপর অ্যারো ফাংশনের ভিতরে আমরা কোনো একটা কাজ করব। এইখানে sum-এর সাথে num-এর যোগ করার মতো করে চিন্তা করলে accumulator-এর সাথে currentValue যোগ করতেছস। আর accumulator একটা কঠিন শব্দ, এইটার মানে একসাথে রাখা বা একসাথে বানানো বা যোগ করাও চিন্তা করতে পারস।

কিছুটা আইডিয়া পাইছস। এখন কলব্যাক ফাংশনকে একসাথে দেখলে একটু ভেজাইল্লা লাগবে। তাও এক চোখ বন্ধ করে দেখে ফেল। কী আছে জীবনে?

javascript
numbers.reduce((accumulator, currentValue) => doSomeWork, initialValue);

আবারো বলতেছি, Reduce দুইটা প্যারামিটার নেয়। প্রথমটা হইতেছে একটা callback function, আর দ্বিতীয়টা হইতেছে initial value। আর আমাদের যে callback function আছে, তার ভিতরে আরও দুইটা প্যারামিটার থাকবে— একটা accumulator, আর অন্যটা current value।

তুই জানস, লুপ চলতে চলতে এক একটা করে উপাদান আসবে। এইখানে লুপ চলতে চলতে যখন যে উপাদানটা একটা ভেরিয়েবলের নাম দিয়ে আসবে, সেটাই থাকবে currentValue-এর মান হিসেবে। আরও সিম্পলভাবে বললে, লুপের মধ্যে num হিসেবে যেটা দেখছিলি, সেটাই আসলে currentValue, আর কিছু না।

অন্যদিকে accumulator হচ্ছে, লুপ চলতে চলতে কিছু কাজ (doSomeWork) করার পর যে রেজাল্ট হবে, সেই রেজাল্ট লুপের মধ্যে প্রত্যেকবার আসবে accumulator-এর মান হিসেবে।

তাইলে doSomeWork দিয়ে বুঝা যায় যে, লুপ চলতে চলতে প্রত্যেকবার কাজটা বারবার করবে।

আর initialValue হচ্ছে, ফাইনাল যে কাজটা করবে, সেটা লুপ শুরু হওয়ার আগে কত মান থাকবে। প্রাথমিক মান চিন্তা করতে পারস।

এত এত বিশাল ইতিহাস দেখার পর এইবার ছোট করে একটা উদাহরণ দেখে ফেল।

javascript
const numbers = [4, 5, 7, 1, 2, 66];
const total = numbers.reduce((sum, num) => sum + num, 0);
console.log(total);

Output: 85;

একটু ভেজাল হলেও জেনে রাখা ভালো।

javascript
const numbers = [32, 34, 73, 13, 22, 5];
const total = numbers.reduce((p, c) => p + c, 0);
console.log(total);

Output: 179;

এতক্ষণ যা শিখলি, map, forEach, filter, find, reduce— এইগুলা সিম্পল সংখ্যা বা স্ট্রিংয়ের জন্য ইউজ করছস। এখন সিম্পল সংখ্যার জন্য না করে অনেকগুলা অবজেক্টের একটা অ্যারের জন্য করে ফেল। অনেকটা সেইম জিনিসই। জাস্ট আগে map ফিল্টারের ভিতরে সরাসরি উপাদান পাইতি, এখন অবজেক্ট পাবি। আর অবজেক্ট থেকে প্রোপার্টির মান এক্সেস করার জন্য ডট নোটেশন বা বক্স নোটেশন ইউজ করবি, আর কিছু না।

আমাদের কাছে একটা অ্যারের মধ্যে বেশ কিছু object আছে।

javascript
const products = [
  { id: 1, name: "lenovo", price: 65000 },
  { id: 2, name: "dell", price: 45000 },
  { id: 3, name: "hp", price: 40000 },
  { id: 4, name: "mac", price: 165000 },
];

আমি যদি শুধু প্রোডাক্টগুলার নাম পেতে চাই। খুবই সিম্পল।

javascript
const names = products.map((p) => p.name);
console.log(names);

Output: ["lenovo", "dell", "hp", "mac"];

তুই চাইলে এই একই সিস্টেমে সবগুলার দাম বা সবগুলার আইডি বা অন্য কোনো প্রোপার্টির মান একটা অ্যারের মধ্যে রেখে দিতে পারবি।

এইবার তুই যদি ঐসব প্রোডাক্টগুলো পেতে চাস, যেগুলার দাম 50 হাজার টাকার ওপরে। খুব সহজেই ফিল্টার করতে পারবি।

javascript
const expensive = products.filter((p) => p.price > 50000);
console.log(expensive);

Output: [
  { id: 1, name: "lenovo", price: 65000 },
  { id: 4, name: "mac", price: 165000 },
];

আবার যদি 50 হাজার টাকার নিচে কোনো একটা প্রোডাক্ট হলেই হবে, তাহলে তুই ফাইন্ড ইউজ করে ফেলতে পারবি। তোকে রিটার্ন হিসেবে সেই উপাদান বা সেই অবজেক্টটা দিয়ে দিবে।

javascript
  const affordable = products.find(p => p.price < 50000);
  console.log(affordable);

Output:
  { id: 2, name: 'dell', price: 45000 }

আবার তুই যদি সবগুলা প্রোডাক্টের দামের যোগফল পেতে চাস, তাহলে তুই তোর প্রিয় reduce ইউজ করে ফেলতে পারবি।

javascript
const total = products.reduce(
  (accumolator, current) => accumolator + current.price,
  0,
);
console.log(total);

Output: 315000;

Practice:

  1. তোর কাছে একটা array আছে: [5, 10, 15, 20, 25]। তুই একটা প্রোগ্রাম লিখে দেখ, সব সংখ্যার যোগফল কত হয় reduce দিয়ে।
  2. তুই একটা দোকানের মালিক। তোর পণ্যগুলা: [{name: 'shampoo', price: 100}, {name: 'soap', price: 50}, {name: 'toothpaste', price: 75}]। সব পণ্যের মোট দাম বের কর reduce ব্যবহার করে।
  3. ধর, তুই একটা product-এর list বানাইছিস: [{name: 'Pen', price: 5}, {name: 'Book', price: 50}, {name: 'Bag', price: 100}]। এবার সব পণ্যের দাম যোগ কর reduce দিয়ে।
  4. reduce দিয়ে [1, 2, 3, 4, 5] সব সংখ্যার গুণফল বের কর।
  5. [10, 20, 30, 40, 50] এই অ্যারে reduce ব্যবহার করে সর্বোচ্চ মান বের কর।
  6. একটা অ্যারে বানা [100, 200, 300, 400]। reduce দিয়ে সব সংখ্যার যোগফল বের কর এবং ইনিশিয়াল ভ্যালু হিসেবে 50 ব্যবহার কর।

13-4: চট করে Sort কর

Sort

যদি ফুঁ দিলে ভদ্রলোক হয়ে যাওয়া যেত, তাহলে সবাই ভদ্রলোক হয়ে থাকত। আবার ফুঁ দিলে যদি অগুছালো বাসাবাড়ি গুছানো হয়ে যেত, তাহলে সবার পড়ার টেবিল, জামা-কাপড়, বিছানা, চৌকির তলা— সব পারফেক্টভাবে গুছানো থাকত।

যদিও ফুঁ দিলে গুছানো হবে, এইটা অনেকেই আশা করতে পারে না। তবে জাভাস্ক্রিপ্টে ফুঁ দিলে অনেক কিছু সাজানো-গুছানো হয়ে যেতে পারে।

এই ফুঁ দেয়ার জন্য জাস্ট একটা অ্যারের নামের পর একটা ডট চিহ্ন(.) দিয়ে তারপর sort লিখে এরপর দুইটা ব্র্যাকেট দিয়ে ফেলবি। নিচের মতো। তাহলেই অনেক সময় কাজ হয়ে যাবে।

ধর, তোর কাছে বন্ধুর নামের একটা তালিকা আছে। এখন তুই চাইতেছিস নামগুলারে alphabetically সাজাতে।

javascript
const friends = ["Zara", "Bob", "Anna", "Chris"];
friends.sort();
console.log(friends);

Output: ["Anna", "Bob", "Chris", "Zara"];

চমৎকারভাবে নামগুলো alphabetically সাজানো হয়ে গেল।

javascript
const numbers = [3, 5, 4, 2, 7, 1, 6, 9];
numbers.sort();
console.log(numbers);

Output: [1, 2, 3, 4, 5, 6, 7, 9];

এইখানেও দেখবি, একদম চমৎকারভাবে সংখ্যাগুলো সাজানো হয়ে গেল। ছোট থেকে বড়।

এইটুক পর্যন্ত জীবন ছিল মধুময়। যদি কোনো বড়সড় সংখ্যা চলে আসে, তাহলে একটু ভেজাল লেগে যায়। যেমন—

javascript
const numbers = [30, 5, 100, 12];
numbers.sort();
console.log(numbers);

Output: [100, 12, 30, 5];

দেখলি? এটা ঠিকমতো কাজ করল না। কারণ, এটা string হিসেবে ধরে সাজাইছে। স্ট্রিং হিসেবে সাজায় বলতে বুঝায়, তুই যদি litu, asif, jamal, abidur, borhan-কে সাজাতে চাস।

javascript
const names = ["litu", "asif", "jamal", "abidur", "borhan"];
names.sort();
console.log(names);

Output: ["abidur", "asif", "borhan", "jamal", "litu"];

কারণ, তুই নামের প্রথম অক্ষর দিয়ে তুলনা করতে করতে যাবি। অর্থাৎ যদি litu নামটা ছোটও হয়, তাহলেও সে পরে থাকবে, আর abidur-এর নাম বড় হলেও সে যেহেতু a দিয়ে শুরু হয়েছে, সেটা আগে চলে আসবে। আবার ইংরেজি অক্ষরের মধ্যে b আগে আর j পরে। তাই সিরিয়ালে jamal-এর আগে borhan চলে যাবে।

এইটা তো বুজছস?

দুঃখজনকভাবে জাভাস্ক্রিপ্ট সংখ্যার তুলনা পুরা সংখ্যা দিয়ে না করে প্রথম অঙ্ক দিয়ে করে। সেজন্য সে 30 যেহেতু 3 দিয়ে শুরু হয়েছে, সে 30-কে 5-এর আগে রাখবে। একইভাবে 100 যেহেতু 1 দিয়ে শুরু হইছে, তাই 100-কে 30-এর আগে রাখবে। কী অদ্ভুত, তাই না?

তবে মন খারাপ করার কিছু নাই। এইটার ভালো একটা সমাধান আছে। সেই সমাধান হচ্ছে— তুই একটা কলব্যাক ফাংশন লিখে দিবি, তারপর তোর ইচ্ছামতো তুলনা করে তোর মনমতো সিস্টেমে সাজাতে পারবি।

এই কলব্যাক ফাংশনকে বলে compare function। কারণ, এইটা দিয়ে অ্যারের উপাদানগুলোকে তুলনা করে বা কম্পেয়ার করে।

এই কম্পেয়ার ফাংশনে দুইটা প্যারামিটার থাকে। তুই প্যারামিটারের যেকোনো নাম দিতে পারবি। তারপর ফাংশনের ভিতরে তুলনা করবি। আমরা যেহেতু সংখ্যার তুলনা করব। পুরা সংখ্যার তুলনা করব। সেক্ষেত্রে একটা থেকে আরেকটা প্যারামিটরকে বিয়োগ করে দিলেই হবে।

javascript
  (a, b) ⇒ a - b

sort-এর ভিতরে এই সিম্পল ফাংশনটা লিখলেই সংখ্যার মান অনুসারে সাজানোর কাজটা হয়ে যাবে।

javascript
const numbers = [30, 5, 100, 12];
numbers.sort((a, b) => a - b);
console.log(numbers);

Output: [5, 12, 30, 100];

এইবার compare function-এর ভিতরের কাহিনি এক্সপ্লেইন করি। এই কম্পেয়ার ফাংশন, অ্যারে থেকে দুইটা করে উপাদান নিয়ে তুলনা করবে। অর্থাৎ (a, b) ⇒ a - b সে আসলে অ্যারের দুইটা উপাদান নিয়ে তুলনা করে। আর পুরা সংখ্যা নিয়ে তুলনা করতে গেলে তিনটা জিনিস হতে পারে।

হতে পারে a - b-এর মান পজিটিভ সংখ্যা। পজিটিভ মানে হচ্ছে a বড় আর b ছোট, তাই a - b পজিটিভ হলে সে অ্যারের মধ্যে a-কে পরে নিয়ে যাবে, আর b-কে আগে নিয়ে যাবে।

আবার হতে পারে a - b-এর মান নেগেটিভ সংখ্যা। নেগেটিভ মানে হচ্ছে a ছোট আর b বড়, তাই a - b নেগেটিভ। আর এমনটা হলে সে অ্যারের মধ্যে a-কে আগে নিয়ে যাবে, আর b-কে পরে নিয়ে যাবে।

আরেকটা জিনিস হতে পারে, a - b-এর মান শূন্য। অর্থাৎ a আর b দুইটার মানই সেইম। অর্থাৎ a - b-এর মধ্যে ডিফারেন্স নাই। তখন সে a-এর জায়গায় a-কে রেখে দিবে। সরাবে না। একইভাবে b-এর জায়গায় b-কে রেখে দিবে। জায়গা চেইঞ্জ করবে না।

সংখ্যা সাজানো (Descending Order)

এই কম্পেয়ার ফাংশনটা অনেক কাজে লাগে। a - b দিলে সে সংখ্যাগুলোকে ছোট থেকে বড় আকারে অর্থাৎ Ascending order-এ সাজাবে। আবার এই কম্পেয়ার ফাংশন ইউজ করে তুই চাইলে ডিসেন্ডিং অর্ডারে (Descending order) সাজাতে পারবি। অর্থাৎ বড় থেকে ছোট ক্রমানুসারে সাজাতে গেলে ইচ্ছা করে কম্পেয়ার ফাংশনের ভিতরে a - b না দিয়ে b - a দিয়ে দিবি। তাহলে সে পজিটিভ হলে বড় সংখ্যাটাকে আগে রাখবে, আর ছোটটাকে পিছনে রাখবে। অর্থাৎ Ascending-এর উল্টাভাবে কাজ করবে।

javascript
const numbers = [30, 5, 100, 12];
numbers.sort((a, b) => b - a);
console.log(numbers);

Output: [100, 30, 12, 5];

অ্যাডভান্সড ব্যবহার : Objects সাজানো

কম্পেয়ার ফাংশনের আরেকটা সুপার পাওয়ার হচ্ছে, বিভিন্ন ধরনের জিনিসও তোর ইচ্ছামতো sort করতে পারবি। যেমন ধর, তোর কাছে বন্ধুর লিস্ট আছে, যেখানে প্রত্যেক বন্ধুর নাম আর বয়স দেয়া আছে। তুই চাইতেছিস তাদের বয়স অনুযায়ী ascending অর্ডারে সাজাতে।

javascript
const friends = [
  { name: "Zara", age: 25 },
  { name: "Bob", age: 20 },
  { name: "Anna", age: 30 },
];

friends.sort((a, b) => a.age - b.age);
console.log(friends);

Output: [
  { name: "Bob", age: 20 },
  { name: "Zara", age: 25 },
  { name: "Anna", age: 30 },
];

এইভাবে sort দিয়ে খুব সহজেই (বা একটু কষ্ট করে কম্পেয়ার ফাংশন দিয়ে) তুই সাজিয়ে ফেলতে পারবি তোর ছোট্ট সুখের জাভাস্ক্রিপ্টের ঘর।

Practice:

  1. অ্যারেটাকে ছোট থেকে বড় সাজিয়ে দে: const numbers = [50, 12, 25, 8, 15];
  2. একটা array numbers = [13, 2, 45, 9, 6]; ব্যবহার করে descending order-এ sort কর।
  3. বন্ধুদের age অনুসারে sorting করে দেখা const friends = [{name: 'Ali', age: 29}, {name: 'Sara', age: 22}, {name: 'Tariq', age: 35}];
  4. একটা নামের array দিয়ে প্রতিটি নামকে alphabetically সাজিয়ে দেখাও: const names = ['nabil', 'zubayer', 'sarwar', 'delwar'];

13-5: উল্টাপথের reverse

তোর কাছে names নামের একটা অ্যারে আছে এবং তুই এটাকে উল্টাতে চাস। উল্টানো মানে রিভার্স করতে চাস। অর্থাৎ যে একদম শেষে আছে, সে সবার আগে আসবে। আবার যে সবার আগে আছে, সে সবার শেষে যাবে। এইভাবে উল্টে যাবে এমন একটা বিষয়। এইটা করার জন্য জাস্ট অ্যারের নাম, এরপর ডট চিহ্ন দিয়ে reverse লিখে দুইটা ব্র্যাকেট দিয়ে দিবি। ব্যস কাজ শেষ।

javascript
const names = ["Alice", "Bob", "Charlie", "David"];
names.reverse();
console.log(names);

Output: ["David", "Charlie", "Bob", "Alice"];

সেইম সিস্টেমে তুই চাইলে সংখ্যার একটা অ্যারেকেও রিভার্স করতে পারবি। কোনো ভেজাল নাই।

javascript
const numbers = [10, 20, 30, 40, 50];
numbers.reverse();
console.log(numbers);

Output: [50, 40, 30, 20, 10];

Practice:

  1. একটা names array দিয়ে নামগুলো reverse করে দেখ: const names = ['Zara', 'Tariq', 'Amir', 'Lina'];
  2. numbers-এর array দিয়েও reverse কর: const numbers = [1, 4, 9, 7];
  3. অবজেক্টওয়ালা অ্যারেকে reverse কর: const users = [{name: 'Ali', age: 30}, {name: 'Sara', age: 25}, {name: 'Tariq', age: 35}];

13-6: নাইস নাইস slice splice

তোর কাছে একটা পিজ্জা আছে, যার প্রতিটা স্লাইসকে তুই আলাদা আলাদাভাবে চিহ্নিত করতে পারিস। এখন তুই ঠিক করলি, মাঝের কয়েকটা স্লাইস আলাদা করে রাখবি। slice() ঠিক এই কাজটাই করে। এটা কোনো অ্যারের নির্দিষ্ট অংশকে কেটে আলাদা করে ফেলে, কিন্তু মূল অ্যারেকে বদলায় না।

স্লাইসের মধ্যে সাধারণত 2টা প্যারামিটার থাকে। প্রথম প্যারামিটার হচ্ছে, কোন ইনডেক্স থেকে কাটা শুরু করবে, আর কোন ইনডেক্সের আগ পর্যন্ত কাটতে চাস।

নিচে একটা অ্যারে আছে—

javascript
const pizzaSlices = ["Bread", "Cheese", "Veggie", "BBQ", "Meet"];
const selectedSlices = pizzaSlices.slice(1, 4);

console.log(selectedSlices);
console.log(pizzaSlices);

Output: ["Cheese", "Veggie", "BBQ"][
  ("Bread", "Cheese", "Veggie", "BBQ", "Meet")
];

তবে দুইটা প্যারামিটার দিতেই হবে, এমন কোন কথা নাই। তুই চাইলে একটা প্যারামিটার দিতে পারস। তাহলে সেই ইনডেক্স থেকে একদম শেষ পর্যন্ত যত উপাদান আছে, সবগুলাকে দিয়ে দিবে।

javascript
const selectedSlices = pizzaSlices.slice(2);
console.log(selectedSlices);

Output: ["Veggie", "BBQ", "Meet"];

আর যদি কোনো প্যারামিটার না দিস, তাহলে পুরো অ্যারেটাই কপি হয়ে যাবে।

javascript
const fullPizza = pizzaSlices.slice();
console.log(fullPizza);

Splice:

স্লাইস থেকে তুই উপাদান বের করতে পারবি, তবে সে অ্যারেকে চেইঞ্জ করে না। অন্যদিকে splice মূল অ্যারেকে বদলে ফেলে। প্লাস এইটা দিয়ে অ্যারে থেকে আইটেম মুছে ফেলতে বা রিমুভ করতে পারবি। এমনকি অ্যারের যেকোনো জায়গায় (এমনকি শুরু, শেষ বা মাঝখানে) নতুন উপাদান যোগ করতে পারবি।

ধর, তোর কাছে একটা সিনেমার লিস্ট আছে। তুই ঠিক করলি, মাঝখান থেকে একটা সিনেমা বাদ দিবি, আর তার জায়গায় নতুন সিনেমা ঢোকাবি। splice-এ সাধারণত 2টা প্যারামিটার দেয়। প্রথম প্যারামিটার দিয়ে বুঝায়, কোন ইনডেক্স থেকে শুরু করবে। আর সেকেন্ড প্যারামিটার দিয়ে বুঝায়, তুই কয়টা উপাদান ডিলিট করতে চাস।

প্লাস আরেকটা জিনিস মনে রাখবি, splice কিন্তু যে অ্যারের ওপরে এপ্লাই করবি, সে সেই অ্যারেকে চেইঞ্জ করে ফেলবে, আর যে যে উপাদান রিমুভ করবে, তাদেরকে রিটার্ন করবে।

নিচের অ্যারের মধ্যে আমরা splice করছি 2 ইনডেক্স থেকে, তারপর 3টা উপাদান রিমুভ করে ফেলছি। আবার পরের লাইনে movies-কে কনসোল লগ করে দেখছি, movies-এর মধ্যে উপাদানগুলো সরে গেছে। অর্থাৎ অরিজিনাল অ্যারে কিন্তু চেইঞ্জ হয়ে গেছে।

javascript
const movies = ["Jaws", "Rocky", "Alien", "Avatar", "Coco", "Up", "It"];
const removed = movies.splice(2, 3);
console.log(removed);
console.log(movies);

Output: ["Alien", "Avatar", "Coco"][("Jaws", "Rocky", "Up", "It")];

splice-এর আরেকটা বাড়তি সুবিধা হচ্ছে, যেখান থেকে উপাদান রিমুভ করতে চাস, সেখানে নতুন উপাদান যোগ করতে পারবি। যতগুলা ইচ্ছা। জাস্ট সেকেন্ড প্যারামিটারের পর ইচ্ছামতো উপাদান দিয়ে ফেলবি।

javascript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const deleted = numbers.splice(3, 3, 99, 100);
console.log(deleted);
console.log(numbers);

Output: [4, 5, 6][(1, 2, 3, 99, 100, 7, 8, 9, 10)];

Practice:

  1. একটা অ্যারে বানা fruits নামে, যেখানে উপাদানগুলো Apple, Banana, Cherry, Date। এবার slice ব্যবহার করে Banana থেকে Cherry পর্যন্ত বের করে আলাদা অ্যারেতে রাখ।
  2. cars নামে একটা অ্যারে আছে, যার মধ্যে Tesla, BMW, Toyota, Ford আছে। slice দিয়ে শুধু Tesla আর BMW রাখ।
  3. movieList নামে একটা অ্যারে, যেখানে Inception, Titanic, Joker, Avatar, Interstellar আছে। splice দিয়ে Joker আর Avatar বাদ দিয়ে তার জায়গায় Batman আর Superman যোগ কর।
  4. players নামে একটা অ্যারে, যার উপাদান Messi, Ronaldo, Neymar, Mbappe। splice ব্যবহার করে Neymar বাদ দিয়ে Halland যোগ কর।

Released under the MIT License.