Module ৩ · string আর template literal
কোনটা কখন: method না loop, template না +, আর কখন regex
এই lesson-এ যা শিখবে
- Text-এ পরিষ্কার একটা separator থাকলে
splitআরjoinবেছে নিতে পারবে, আর না থাকলেinQuotesflag-ওয়ালা একটা character loop। substringআরsubstr-এর বদলেslice,includesনাকিindexOf, template literal,+নাকিjoin, আর<নাকিlocaleCompare: প্রতিটা জোড়া থেকে বেছে নিতে পারবে, কারণসহ।- Chart আর flowchart দিয়ে text ভাঙতে পারবে,
[...s]দিয়ে emoji আস্ত রাখতে পারবে, আর চিনতে পারবে কখন একটা কাজ regular expression-এর কাজ হয়ে গেছে।
Kenji দাবা ক্লাবের member list একটা spreadsheet থেকে import করে। Importer-টা সে লিখেছে দশ মিনিটে, আর বানিয়েছে fast: প্রতি লাইনে একটা split(",")। তারপর ক্লাবের treasurer জিজ্ঞেস করে, Amara কেন Lake Road" নামের একটা শহরে থাকে। ওর ঠিকানা 12, Lake Road-এর ভিতরে একটা comma ছিল, আর তার পরের প্রতিটা কলাম এক ঘর ডানে সরে গেছে। Fast হলেও ভুল তো ভুলই। এই lesson আগে খুঁজে বের করে ঠিক কোথায় split আর কাজ করে না, তারপর আরও ছয়টা judgement call নেয়, প্রতিটার জন্য একটা program।
যেখানে split আর কাজ করে না
CSV file-এ কোনো field-এর ভিতরে comma থাকলে field-টাকে double quote-এ রাখা হয়। এটা CSV-র standard, RFC 4180-এর নিয়ম। split(",") quote-এর কিছুই বোঝে না, তাই quote-এর ভিতরেও কেটে ফেলে।
const record = "Kenji,\"12, Lake Road\",Dhaka";
const fields = record.split(",");
console.log(fields.length);
console.log(fields);
4
[ 'Kenji', '"12', ' Lake Road"', 'Dhaka' ]
ঢুকেছিল তিনটা field, বের হলো চারটা, আর quote চিহ্নগুলো টুকরোর গায়ে লেগে আছে। কোনো error আসে না, তাই bug-টা সোজা spreadsheet-এ চলে যায়। split ঠিক tool শুধু তখনই, যখন separator কোনো field-এর ভিতরে কখনো আসতে পারে না। এখানে আসতে পারে, তাই এমন একটা reader লাগবে যে মনে রাখে সে quote-এর ভিতরে আছে কিনা।
যে loop জানে সে quote-এর ভিতরে আছে
Flag হলো একটা boolean variable, যেটা loop চলার সময় একটা কথা মনে রাখে। এখানে কথাটা হলো "আমি কি quote-এর ভিতরে?"। একটা quote চিহ্ন flag-টা উল্টে দেয়। Comma একটা field শেষ করে শুধু তখন, যখন flag বন্ধ। বাকি প্রতিটা character চলতি field-এ যোগ হয়।
Quote বোঝে এমন loop
let inQuotes = false;
for (const ch of record) {
if (ch === "\"") inQuotes = !inQuotes;
else if (ch === "," && !inQuotes) end the field
else add ch to the field
}
inQuotesশুরু হয়falseদিয়ে, কারণ প্রতিটা record শুরু হয় quote-এর বাইরে।"\""হলো একটা double quote-ওয়ালা string; backslash-টা ওকে string বন্ধ করতে দেয় না।!inQuotestrue হয় শুধু quote-এর বাইরে, তাই quote-এর ভেতরের comma নিছক text।
Program-টা একটা record পড়ে, আর প্রতি লাইনে একটা করে field print করে, পাশে split-এর গোনা আর loop-এর গোনা।
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 = [];
const record = next();
const fields = [];
let field = "";
let inQuotes = false;
for (const ch of record) {
if (ch === "\"") {
inQuotes = !inQuotes;
} else if (ch === "," && !inQuotes) {
fields.push(field);
field = "";
} else {
field += ch;
}
}
fields.push(field);
out.push(`split(","): ${record.split(",").length} pieces`);
out.push(`the loop: ${fields.length} fields`);
for (let i = 0; i < fields.length; i++) {
out.push(`${i + 1}: ${fields[i]}`);
}
console.log(out.join("\n"));
split(","): 4 pieces
the loop: 3 fields
1: Kenji
2: 12, Lake Road
3: Dhaka
এই output-টা input Kenji,"12, Lake Road",Dhaka-এর জন্য। Quote-এর ভেতরের comma ঠিকানার ভিতরেই রয়ে গেছে, আর quote চিহ্নগুলো বাদ পড়েছে। Loop-এর পরের শেষ push শেষ field-টা বাঁচায়, কারণ ওটাকে কোনো comma শেষ করে না।
একটা ফাঁক এখনো আছে, সেটা খোলাখুলি বলি। RFC 4180 quote-এর ভেতরের field-এ একটা quote লেখে দুইটা quote দিয়ে, ""। এই loop flag-টা দুইবার উল্টায় আর দুটোই হারিয়ে ফেলে, তাই "She said ""hi""" বের হয় She said hi হয়ে। অনুশীলন 3 তোমাকে ওটা রাখতে বলবে।
পুরো সিদ্ধান্তটা এক ছবিতে। উপর থেকে শুরু করো, প্রতিটা প্রশ্নের উত্তর দাও, আর প্রথম "হ্যাঁ"-তে থামো।
slice, substring আর substr: chart-টা
Lesson 02-এ একটা string থেকে একটা টুকরো কাটার তিনটা উপায় দেখেছ। সহজ call-গুলোতে তিনজন একমত, বাকিগুলোতে না। Chart-টা একই ছয়টা call তিনটার উপরই চালায়, যাতে পার্থক্যটা চোখে পড়ে।
প্রতিটা লাইন এক সারির তিনটা উত্তর bracket-এর ভিতরে print করে, যাতে খালি string-ও চোখে পড়ে।
const s = "template";
console.log(`(1, 4): [${s.slice(1, 4)}] [${s.substring(1, 4)}] [${s.substr(1, 4)}]`);
console.log(`(4, 1): [${s.slice(4, 1)}] [${s.substring(4, 1)}] [${s.substr(4, 1)}]`);
console.log(`(-3): [${s.slice(-3)}] [${s.substring(-3)}] [${s.substr(-3)}]`);
console.log(`(2, -1): [${s.slice(2, -1)}] [${s.substring(2, -1)}] [${s.substr(2, -1)}]`);
console.log(`(-4, 2): [${s.slice(-4, 2)}] [${s.substring(-4, 2)}] [${s.substr(-4, 2)}]`);
console.log(`(3): [${s.slice(3)}] [${s.substring(3)}] [${s.substr(3)}]`);
(1, 4): [emp] [emp] [empl]
(4, 1): [] [emp] [l]
(-3): [ate] [template] [ate]
(2, -1): [mplat] [te] []
(-4, 2): [] [te] [la]
(3): [plate] [plate] [plate]
(4, 1) সারিটাতেই Bob-এর off-by-one লুকিয়ে আছে। slice বলে "ওখানে কিছু নেই", আর খালি ফলটা test-এ ধরা পড়ে। substring চুপচাপ সংখ্যা দুটো অদলবদল করে দেয়, আর ভুল টুকরোটাকেও ঠিকঠাক দেখায়। তাই লেখো slice: negative শেষ থেকে গোনে, শেষ ঘরটা বাদ যায়, আর তোমার অজান্তে কিছুই অদলবদল হয় না।
"আছে কিনা"-র জন্য includes, "কোথায়"-র জন্য indexOf
দুটোই string-এর ভিতরে খোঁজে। includes একটা হ্যাঁ-না প্রশ্নের উত্তর দেয়, boolean দিয়ে। indexOf উত্তর দেয় "কোথায়?", একটা number দিয়ে, আর "কোথাও না" হলে -1। তুমি কোন প্রশ্নটা করছ, সেটা দেখে বেছে নাও।
@ কি আছে? কোথায় আছে? আর তার সামনের নামটা কী?
const email = "kenji@mail.com";
console.log(email.includes("@"));
console.log(email.indexOf("@"));
console.log(email.slice(0, email.indexOf("@")));
console.log(email.includes("#"), email.indexOf("#"));
true
5
kenji
false -1
"আছে কিনা"-র জন্য includes পড়তে প্রশ্নটার মতোই, আর lesson 02-এর ফাঁদে পড়ে না, যেখানে if (s.indexOf(x)) index 0-এর match-কে false ধরে। জায়গাটা লাগলে, যেমন নামটার জন্য, tool হলো indexOf, আর ওর উত্তরকে -1-এর সাথে মেলাও।
Value-ওয়ালা লাইনে template, দুই টুকরোয় +, অনেক টুকরোয় join
Text বানানোর তিনটা উপায়, আর প্রত্যেকটার নিজের কাজ। Template literal পুরো লাইনটা যেভাবে print হবে সেভাবেই দেখায়, শুধু value-র জায়গাগুলো খালি রেখে, তাই পড়তে সহজ। দুইটা টুকরোর জন্য + ঠিক আছে। join একটা পুরো array-কে একটাই separator দিয়ে জোড়া দেয়।
একই বাক্য + দিয়ে আর template হিসেবে, তারপর একটা ছোট label, তারপর একটা array থেকে একটা CSV সারি।
const name = "Amara";
const books = 3;
const total = 1940;
console.log("Dear " + name + ", your " + books + " books cost Tk " + total + ".");
console.log(`Dear ${name}, your ${books} books cost Tk ${total}.`);
console.log("Tk " + total);
const row = ["Amara", "16", "Dhaka", "amara@mail.com"];
console.log(row.join(","));
Dear Amara, your 3 books cost Tk 1940.
Dear Amara, your 3 books cost Tk 1940.
Tk 1940
Amara,16,Dhaka,amara@mail.com
প্রথম দুইটা লাইন একই text print করে। +-ওয়ালা লাইনে গুনে দেখো কয়টা quote চিহ্ন আর space তোমাকে হাতে বসাতে হয়েছে; একটা space বাদ পড়লেই রসিদে লেখা হবে 3books। দুই টুকরোর জন্য "Tk " + total ছোট, পরিষ্কার। একটা list-এর জন্য join প্রতি দুই item-এর মাঝে ঠিক একটা separator বসায়, দুই মাথায় একটাও না।
অনেক team template-কে নিয়মই বানিয়ে ফেলে। ESLint-এর prefer-template নিয়ম text আর value-র মাঝে + দেখলে আপত্তি তোলে, আর লাইনটা তোমার হয়ে নতুন করে লিখেও দিতে পারে। এটা team-এর মেনে নেওয়া একটা style-এর ব্যাপার, speed-এর নিয়ম না: দুই লাইনই একই string বানায়।
Code-এর জন্য <, মানুষের জন্য localeCompare বা Intl.Collator
Lesson 02 দেখিয়েছে, < দুইটা string-কে code ধরে ধরে মেলায়, তাই প্রতিটা বড় হাতের অক্ষর সব ছোট হাতের অক্ষরের আগে আসে। Code বা ID-র জন্য সেটা ঠিক। কিন্তু মানুষ পড়বে এমন নামের list-এর জন্য ভুল। localeCompare মেলায় একটা ভাষা যে ক্রমে শব্দ সাজায়, সেভাবে। Intl.Collator হলো একটা object, যেটা একই তুলনা করে, শুধু setting-গুলো একবারে ঠিক করা থাকে, তাই লম্বা sort-এ মানানসই।
Member-রা নিজের নাম নিজে টাইপ করেছে, কেউ বড় হাতের অক্ষরে, একজনের নামে accent আছে। Default sort() text হিসেবে code-এর ক্রমে মেলায়। sort-এর ভেতরের arrow function-টা সেই ছোট রূপ, যেটা Module 5 বুঝিয়ে বলবে।
const names = ["zara", "Amara", "Émile", "bob", "Kenji"];
names.sort();
console.log(`< order: ${names.join(", ")}`);
names.sort((x, y) => x.localeCompare(y));
console.log(`localeCompare: ${names.join(", ")}`);
const collator = new Intl.Collator("en");
names.sort(collator.compare);
console.log(`Intl.Collator: ${names.join(", ")}`);
const photos = ["photo10.jpg", "photo9.jpg", "photo2.jpg"];
photos.sort(new Intl.Collator("en", { numeric: true }).compare);
console.log(`numeric: true: ${photos.join(", ")}`);
< order: Amara, Kenji, bob, zara, Émile
localeCompare: Amara, bob, Émile, Kenji, zara
Intl.Collator: Amara, bob, Émile, Kenji, zara
numeric: true: photo2.jpg, photo9.jpg, photo10.jpg
Code-এর ক্রমে bob দাঁড়িয়ে থাকে Kenji-র পেছনে, আর Émile চলে যায় একদম শেষে, কারণ É-এর code 201। localeCompare আর collator দেয় সেই ক্রম, যেটা একজন পাঠক আশা করে। numeric: true option digit-এর সারিকে number হিসেবে মেলায়, তাই photo10 আসে photo9-এর পরে। বাংলা নামের জন্য new Intl.Collator("bn") বাংলার নিয়ম ব্যবহার করে।
Emoji থাকতে পারলে split("")-এর বদলে [...s]
split("") কাটে code unit-এর মাঝে, আর lesson 01 দেখিয়েছে একটা emoji দুইটা code unit নেয়। Spread [...s] আর Array.from(s) বরং string-টা code point ধরে ধরে হাঁটে, তাই emoji আস্ত থাকে। Array-র উপর spread দেখাবে Module 7।
JSON.stringify প্রতিটা টুকরো quote-এর ভিতরে print করে, আর emoji-র একলা অর্ধেককে লেখে \ud83d-এর মতো escape হিসেবে। সরাসরি print করলে একলা অর্ধেক দেখায় একটা replacement character হয়ে।
const name = "Zara 😀";
console.log(name.length);
console.log(JSON.stringify(name.split("")));
console.log(JSON.stringify([...name]));
console.log(JSON.stringify(Array.from(name)));
console.log(JSON.stringify(name.split("").reverse().join("")));
console.log([...name].reverse().join(""));
7
["Z","a","r","a"," ","\ud83d","\ude00"]
["Z","a","r","a"," ","😀"]
["Z","a","r","a"," ","😀"]
"\ude00\ud83d araZ"
😀 araZ
split("") দিয়েছে সাতটা টুকরো, শেষ দুইটা emoji-র এক এক অর্ধেক, আর উল্টাতে গিয়ে অর্ধেক দুটো ভুল ক্রমে বসেছে। [...name] দিয়েছে ছয়টা টুকরো, আর পরিষ্কার একটা উল্টো রূপ। যে text নিয়ম মেনেই ASCII, যেমন judge-এর শব্দগুলো, সেখানে split("") চলে। মানুষ টাইপ করেছে এমন যেকোনো কিছুর জন্য [...s] নাও। বাংলার কার-চিহ্ন তবু অক্ষর থেকে আলাদা হয়ে যায়, আর সেটা lesson 05-এর Intl.Segmenter-এর কাজ।
কখন কাজটা regular expression-এর হয়ে যায়
উপরের প্রতিটা tool নির্দিষ্ট text খোঁজে। কিছু কাজ খোঁজে একটা pattern: যেকোনো সংখ্যক space, যেকোনো digit, শুধু digit দিয়ে বানানো একটা লাইন। এটা regular expression-এর কাজ, text বর্ণনা করার একটা ছোট ভাষা, যেটা Module 16 শেখাবে। এখানে দুইটা এক লাইনের উদাহরণ, শুধু নাম বলা হলো, শেখানো না।
console.log("too many spaces".replace(/\s+/g, " "));
console.log(/^\d+$/.test("2026"), /^\d+$/.test("20x6"));
too many spaces
true false
প্রথমটা পরপর যত space আছে, সব চেপে একটা বানায়। দ্বিতীয়টা জিজ্ঞেস করে, text-টা কি শুরু থেকে শেষ পর্যন্ত শুধু digit। দুটোর প্রত্যেকটাই loop দিয়ে করতে কয়েক লাইন লাগত। চেনার উপায় হলো "যেকোনো" শব্দটা: নিজেকে যখন বলতে শোনো "যেকোনো সংখ্যক" বা "যেকোনো digit", কাজটা তখন pattern হয়ে গেছে।
এটা কোথায় কাজে লাগে
- ESLint।
prefer-templateনিয়ম ("Require template literals instead of string concatenation")"Hello, " + name + "!"দেখলে আপত্তি তোলে। এটা ESLint-এর recommended set-এ নেই, তাই team নিজের ইচ্ছায় চালু করে, আর--fixলাইনটা নতুন করে লিখে দেয়। - Airbnb JavaScript Style Guide। এর নিয়ম 6.3 বলে, string জোড়া দিয়ে বানানোর সময় concatenation-এর বদলে template string ব্যবহার করো, আর ESLint নিয়ম হিসেবে নাম দেয়
prefer-templateআরtemplate-curly-spacing-এর। - RFC 4180। 2005 সালের CSV standard। এর নিয়ম 6 comma-ওয়ালা field-কে double quote-এ রাখে, যেটা Example 1 সামলায়। নিয়ম 7 field-এর ভেতরের একটা quote লেখে দুইটা quote দিয়ে, যেটা সামলাবে অনুশীলন 3।
- ICU। Node আর Chrome
Intl.Collatorবানায় ICU-র উপর, মানে International Components for Unicode library-র উপর, যেটা Unicode Collation Algorithm মেনে চলে। Android একই library app-গুলোর হাতে দেয়android.icu.text.Collatorনামে।
যে ভুলগুলো সবাই করে
১. substr-এর দ্বিতীয় সংখ্যাকে শেষ ঘর ভাবা।
console.log("hello".substr(1, 3), "hello".slice(1, 3));
ell el
substr(1, 3) মানে index 1 থেকে তিনটা character, আর slice(1, 3) থামে index 3-এর আগে। কোনো error নেই, শুধু একটা character বেশি। লেখো slice। পুরনো code-এ তুমি substr পাবে, আর ধরে নেবে এটা পাশের দুইটার মতোই।
২. যেখানে জায়গা লাগবে, সেখানে includes।
const mail = "kenji@mail.com";
const at = mail.includes("@");
console.log(at);
console.log(mail.slice(at));
true
enji@mail.com
slice চেয়েছিল একটা number, পেল true, আর Module 2-এর coercion-এর নিয়মে ওটাকে 1 বানিয়ে নিল। Program একটা অক্ষর কেটে দিব্যি চলতে থাকল। "কোথায়" লাগলে indexOf নাও। তুমি includes-এর দিকে হাত বাড়াবে, কারণ নামটা নতুন আর বেশি বন্ধুসুলভ।
৩. + এর শিকলের ভিতরে number যোগ করা।
console.log("Total: " + 2 + 3);
console.log(`Total: ${2 + 3}`);
Total: 23
Total: 5
+ কাজ করে বাঁ থেকে ডানে, তাই text প্রথমে 2-এর দেখা পায়, আর তার পরের প্রতিটা + text জোড়া দেয়। ${}-এর ভিতরে যোগফলটা আলাদা করে হিসাব হয়। প্রথম লাইনটা তুমি লিখবে, কারণ একটু আগে একটা number দিয়ে এটা ঠিকই কাজ করছিল।
Kenji-র ক্লাব প্রতিটা member-এর email এক লাইনে রাখে, comma দিয়ে আলাদা করে। Secretary একটা নাম টাইপ করে খোঁজে, আর জানতে চায় সেটা আছে কিনা, আর থাকলে কোথায়।
Input. লাইন 1-এ list-টা। লাইন 2-এ n। তারপর n-টা লাইন, প্রতিটায় খোঁজার একটা text।
Output. প্রতিটা text-এর জন্য এক লাইন: found at আর তার প্রথম match-এর index, অথবা not found।
Constraints. 1 <= n <= 1000। List-এ 1 থেকে 2000টা character; প্রতিটা text-এ 1 থেকে 50টা।
Sample. List zara@mail.com,bob@mail.com,kenji@mail.com, তারপর 3, তারপর bob, amara আর zara দিলে found at 14, not found আর found at 0।
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"));
আলাদা করে grade হয় না। Sample-এ Zara ইচ্ছে করেই zara-কে শেষে রেখেছে: ওর index 0। দেখে নাও, তোমার program যেন ওটাকে না-থাকা বলে না দেয়।
Amara ক্লাবের newsletter লেখে। মিটিং-এ কে কে এসেছিল, সেটা থাকে ইংরেজির একটা বাক্যে, আর কেউ কেউ sheet-এ দুইবার সই করেছে।
Input. n, তারপর n-টা নাম, প্রতি লাইনে একটা।
Output. এক লাইন: প্রতিটা নাম একবার, প্রথম যে ক্রমে দেখা গেছে সেভাবে। একটা নাম হলে শুধু সেটাই, দুইটা হলে মাঝে and , আর তিন বা তার বেশি হলে মাঝে মাঝে , , শেষটার আগে and ।
Constraints. 1 <= n <= 1000। প্রতিটা নামে 1 থেকে 30টা character।
Sample. Input 4, তারপর Amara, Bob, Amara আর Kenji দিলে Amara, Bob and Kenji।
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"));
আলাদা করে grade হয় না। Array-রও includes আছে, যেটা বারবার আসা নামগুলোতে কাজে লাগবে। তারপর ঠিক করো, কোন অংশটা join, আর কোনটা template।
Kenji-র importer এখন ঠিকানা সামলাতে পারে, কিন্তু ক্লাবের notes কলামে quote-এর ভিতরেও quote আছে। Parser-টা শেষ করো, যাতে RFC 4180-এর ধাঁচের প্রতিটা record ঠিকঠাক বের হয়।
Input. n, তারপর n-টা record, প্রতি লাইনে একটা। Field-গুলো comma দিয়ে আলাদা। একটা field হয় quote ছাড়া (comma বা double quote বাদে যেকোনো character), নয়তো quote-এর ভিতরে। Quote-ওয়ালা field শুরু হয় একটা double quote দিয়ে, আর শেষ হয় এমন একটা double quote দিয়ে, যার পরে একটা comma বা record-এর শেষ। এর ভিতরে যেকোনো character থাকতে পারে, comma-ও, আর পরপর দুইটা double quote মানে একটা। প্রতিটা record ঠিকঠাক গড়া, space field-এরই অংশ, আর খালি record মানে একটা খালি field। এই problem starter-এর lines variant ব্যবহার করে, আর input-এর শেষ লাইন কখনো খালি না।
Output. প্রতিটা record-এর জন্য এক লাইন: field-এর সংখ্যা, তারপর প্রতিটা field square bracket-এর ভিতরে, সবগুলো একটা করে space দিয়ে আলাদা। Quote-ওয়ালা field print হয় বাইরের quote দুটো ছাড়া, আর ভেতরের প্রতিটা জোড়া double quote একটা হয়ে।
Constraints. 1 <= n <= 10000। প্রতিটা record-এ 0 থেকে 500টা printable ASCII character (code 32 থেকে 126)।
Sample. Input 3, তারপর তিন লাইনে Zara,14,Dhaka, "12, Lake Road",Amara,"She said ""hi""" আর ,, দিলে 3 [Zara] [14] [Dhaka], 3 [12, Lake Road] [Amara] [She said "hi"] আর 3 [] [] []।
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
// Read n, then n records. Per record: the field count and every field in brackets.
console.log(out.join("\n"));
csv-line নামে grade হয়। Example 1 থেকে শুরু করো। Quote-এর ভিতরে একটা quote হয় field-এর শেষ, নয়তো একটা জোড়ার প্রথমটা, আর পরের character-টাই বলে দেয় কোনটা।
সচরাচর যে প্রশ্নগুলো আসে
splitকি আমার loop-এর চেয়ে fast?Kenji এটা জিজ্ঞেস করে, আর প্রথম প্রশ্ন হিসেবে এটা ভুল। দুটোই প্রতিটা character-এ একবার করে যায়, আর এই lesson কিছুই মাপেনি। বেছে নাও ঠিক উত্তর দেখে: quote-এর ভেতরের একটা comma
split-কে যেকোনো speed-এই ভুল বানায়।substrযদি পুরনোই হয়, Node এখনো ওটা চালায় কেন?Standard ওটাকে রেখেছে Annex B-তে, মানে সেই অংশে যেখানে পুরনো feature-গুলোর list থাকে, যেগুলো browser-কে রাখতেই হয় যাতে পুরনো page চলে। MDN ওটাকে deprecated বলে চিহ্নিত করে। চলে ঠিকই, আর নতুন code কেন ওটা এড়ায়, chart-টা সেটা দেখায়।
localeCompareনাকিIntl.Collator?একই ভাষা আর একই option-এ দুটো একই ক্রম দেয়। একবারের তুলনার জন্য
localeCompareহাতের কাছে। লম্বা একটা list sort করতে MDN-এর পরামর্শ: একটাIntl.Collatorবানাও, আর ওরcompare-টাsort-কে দাও।Loop-এর বদলে একটা regular expression কি CSV পড়তে পারত?
সাধারণ লাইনের জন্য পারত। Quote-ওয়ালা field আর জোড়া quote এলে pattern-টা পড়তে কঠিন হয়ে যায়, test করা আরও কঠিন। যে loop তুমি ধাপে ধাপে মিলিয়ে দেখতে পারো, বা Papa Parse-এর মতো test করা একটা library, তাতে ভরসা রাখা সহজ।
মূল কথা
- Separator যখন কখনো field-এর ভিতরে বসতে পারে না, তখন
splitআরjoin; যখন পারে, তখনinQuotesflag-ওয়ালা একটা character loop। substring-এর বদলেslice, আরsubstrকখনো না: negative শেষ থেকে গোনে, আর কিছুই অদলবদল হয় না।- "আছে কিনা"-র জন্য
includes, "কোথায়"-র জন্যindexOf, যার উত্তর -1-এর সাথে মেলাতে হয়। - Value-ওয়ালা লাইনের জন্য template literal, দুই টুকরোর জন্য
+, একটা list-এর জন্যjoin। - Code-এর জন্য
<; মানুষ পড়বে এমন নামের জন্যlocaleCompareবাIntl.Collator; emoji থাকতে পারলেsplit("")-এর বদলে[...s]; আর কাজটা যখন "যেকোনো" বলে, তখন regular expression। - আরও গভীরে: Under the Hood, immutability, rope আর UTF-16 (Pro)।
এরপর lesson 05 (Pro) string-এর ভিতরটা খোলে: +=-এর আসল খরচ কত, UTF-16 আর code point, আর মানুষ যে character দেখে সেগুলো। Free পথে এর পরে আসে problems page, এই module-এর দশটা judged problem নিয়ে।
lesson ৪ শেষ
শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।
পরেরটা: ভেতরের কথা: immutability, rope আর UTF-16