JavaScript માં arrays સાથે કામ કરવાનો અર્થ હવે ભાગ્યે જ એવો થાય કે તમારે for loop લખવાની જરૂર છે. ભાષા હવે તમને એવા અભિવ્યક્ત સાધનો (expressive tools) આપે છે જે નેસ્ટેડ (nested) અને અસ્પષ્ટ ડેટાને સ્વચ્છ અને વાંચી શકાય તેવા રૂપાંતરણોમાં ફેરવી દે છે. આ માર્ગદર્શિકા ત્યાંથી શરૂ થાય છે જ્યાં પાયાની બાબતો પૂરી થાય છે—flattening, reducing, querying અને નવા immutable patterns પર ધ્યાન કેન્દ્રિત કરે છે જે તમારા મૂળ ડેટાને અસ્પૃશ્ય રાખે છે.

flat() અને flatMap() સાથે નેસ્ટેડ સ્ટ્રક્ચર્સને Flatten કરવું

વાસ્તવિક દુનિયાના ડેટામાં નેસ્ટિંગ સામાન્ય છે. API માંથી રેકોર્ડ્સ મેળવો, CSV પાર્સ કરો, અથવા કોન્ફિગરેશન ફાઇલ વાંચો, અને તમે ઘણીવાર એરેની અંદર એરે (arrays inside arrays) સાથે અંતે પહોંચશો. flat() એવા લેયર્સને એક સિંગલ લિનિયર લિસ્ટમાં ભેગા કરવા માટે અસ્તિત્વ ધરાવે છે.

નેસ્ટેડ એરે પર flat() કોલ કરો અને તે ડિફોલ્ટ રીતે એક લેવલ ઊંડું flatten કરે છે:

const nested = [[1, 2], [3, 4]];
const flat = nested.flat();
// [1, 2, 3, 4]

મૂળ nested એરે જેવો હતો તેવો જ રહે છે. જો તમારું નેસ્ટિંગ વધુ ઊંડું હોય, તો flat(2) અથવા flat(Infinity) જેવો depth argument પાસ કરો જ્યારે તમને ખબર ન હોય કે કેટલા લેયર્સ છે. આ મેન્યુઅલી રિકર્સિવ હેલ્પર ફંક્શન્સ લખવા કરતાં ઘણું સારું છે.

જ્યારે તમારે એલિમેન્ટ્સને ટ્રાન્સફોર્મ કરવાની અને પરિણામને flatten કરવાની જરૂર હોય, ત્યારે flatMap() એક જ પાસમાં બંને કામ કરે છે. તે દરેક એલિમેન્ટ પર મેપિંગ ફંક્શન ચલાવે છે, અને પછી મળેલા એરેઝને એક લેવલ સુધી flatten કરે છે. અલગ .map().flat() કોલ્સ સાથે આ કરવું શક્ય છે, પરંતુ flatMap() મધ્યવર્તી એરેને સ્કીપ કરે છે, જે મેમરી અને બીજા ઇટરેશનની બચત કરે છે. વાક્યોના એરેને અલગ-અલગ શબ્દોમાં વિભાજિત કરવું એ તેનો એક ક્લાસિક ઉપયોગ છે:

const lines = ['hello world', 'foo bar'];
const words = lines.flatMap(line => line.split(' '));
// ['hello', 'world', 'foo', 'bar']

Filtering અને Mapping: તફાવત જાણો

filter() એક નવો એરે બનાવે છે જેમાં ફક્ત તે જ આઇટમ્સ હોય છે જે તમારી ટેસ્ટ પાસ કરે છે. એલિમેન્ટને રાખવા માટે કોલબેક (callback) એ truthy વેલ્યુ અને તેને કાઢી નાખવા માટે falsy વેલ્યુ રિટર્ન કરવી જોઈએ. તે ક્યારેય સોર્સ એરેને મ્યુટેટ (mutate) કરતું નથી.

const users = [
  { name: 'Ajay', active: true },
  { name: 'Bina', active: false }
];
const activeUsers = users.filter(u => u.active);

filter() ને map() સાથે ભૂલથી સરખાવવું સરળ છે કારણ કે બંને નવા એરે રિટર્ન કરે છે અને કોલબેક સ્વીકારે છે. તફાવત તેમના કરાર (contract) માં છે. map()તે જ લંબાઈના નવા એરે રિટર્ન કરે છે. દરેક એલિમેન્ટને ટ્રાન્સફોર્મ કરો, કન્ડિશનલ ચેક ચલાવો, અને તમે ખરેખર દરેક સ્લોટ માટે true અથવા false જોશો, કારણ કે map() ક્યારેય આઇટમ્સને ફેંકી દેતું નથી—તે ફક્ત તેને બદલે છે. બીજી બાજુ, filter() સંભવિત રીતે ટૂંકો એરે રિટર્ન કરે છે જેમાં માત્ર તે એલિમેન્ટ્સ હોય છે જે તમારી ટેસ્ટ પાસ કરે છે. જો તમે ડેટા દૂર કરવા માંગતા હોવ, તો filter() નો ઉપયોગ કરો. જો તમે સંખ્યા જાળવી રાખીને ડેટાના દરેક ભાગને ટ્રાન્સફોર્મ કરવા માંગતા હોવ, તો map() નો ઉપયોગ કરો.

reduce() સાથે એરેઝને Collapse કરવું

જ્યાં map() અને filter() એરે રિટર્ન કરે છે, ત્યાં reduce() આખી લિસ્ટને એક વેલ્યુમાં રૂપાંતરિત કરે છે. તે ડાબેથી જમણે ચાલે છે, દરેક સ્ટેપમાં એક accumulator ને આગળ લઈ જાય છે.

const totals = [10, 20, 30];
const sum = totals.reduce((acc, current) => acc + current, 0);
// 60

બીજો આર્ગ્યુમેન્ટ—ઉપરના ઉદાહરણમાં 0—તે પ્રારંભિક accumulator વેલ્યુ છે. તેને સ્કીપ કરો, તો JavaScript પ્રથમ એલિમેન્ટને સ્ટાર્ટિંગ પોઈન્ટ તરીકે ઉપયોગ કરશે, અને ઇટરેશન ઇન્ડેક્સ 1 થી શરૂ કરશે. જ્યારે એરે ખાલી હોય ત્યારે આ બાબત લોકોને મૂંઝવણમાં મૂકે છે, કારણ કે ખાલી એરે પર પ્રારંભિક વેલ્યુ વગર reduce() કોલ કરવાથી TypeError આવે છે.

Reduce માત્ર અંકગણિત માટે નથી. તમે તેનો ઉપયોગ ફ્રીક્વન્સી ટેબલ્સ બનાવવા, ઓબ્જેક્ટ્સને મર્જ કરવા, મહત્તમ મૂલ્યો શોધવા અથવા મલ્ટિપલ ટ્રાન્સફોર્મેશન દ્વારા ડેટા પાઇપ કરવા માટે કરી શકો છો. તે એટલું ફ્લેક્સિબલ હોવાથી, કોલબેક નાનો રાખવો અને તમારા વેરિયેબલ્સના નામ સ્પષ્ટ રાખવા ફાયદાકારક છે. એક ગીચ reduce() બ્લોક ઝડપથી તે લૂપ કરતા વાંચવામાં વધુ અઘરો બની જાય છે જેને તેણે રિપ્લેસ કર્યું છે.

Boolean Queries: every() અને some()

ક્યારેક તમારે નવા એરેની જરૂર નથી હોતી; તમારે કલેક્શન વિશે હા કે ના માં જવાબની જરૂર હોય છે. every() ફક્ત ત્યારે જ true રિટર્ન કરે છે જો તમામ એલિમેન્ટ્સ તમારી શરત સંતોષતા હોય. જો પહેલો જ એલિમેન્ટ નિષ્ફળ જાય, તો તે તરત જ અટકી જાય છે અને false રિટર્ન કરે છે.

const scores = [85, 92, 78];
const allPassed = scores.every(s => s >= 50);

some() તેનું પ્રતિબિંબ છે. જો ઓછામાં ઓછો એક એલિમેન્ટ ટેસ્ટ પાસ કરે તો તે true રિટર્ન કરે છે. જો મેચ મળી જાય, તો તે તરત જ શોર્ટ-સર્કિટ (short-circuit) થઈ જાય છે.

const hasPerfectScore = scores.some(s => s === 100);

વેલિડેશન ચેક્સ માટે આ મેથડ્સ મેન્યુઅલ લૂપ્સનું સ્થાન લે છે. દરેક ફોર્મ ફીલ્ડ ભરાયેલું છે તેની ખાતરી કરવાની જરૂર છે? every() નો ઉપયોગ કરો. કોઈ યુઝર પાસે એડમિન રાઈટ્સ છે તે જાણવાની જરૂર છે? some() નો ઉપયોગ કરો.

Mutation વગર Inspection અને Update કરવું

આધુનિક JavaScript ઇમ્યુટેબિલિટી (immutability) તરફ વધુ ઝુકાવ ધરાવે છે. આ મેથડ્સ તમને મૂળ એરેને અડ્યા વગર તેને ઇન્સ્પેક્ટ અથવા અપડેટ કરવામાં મદદ કરે છે.

keys() એરેના ઇન્ડેક્સ ધરાવતા ઇટરેટર ઓબ્જેક્ટને રિટર્ન કરે છે. entries() [index, value] જોડીઓના ઇટરેટરને રિટર્ન કરે છે. જ્યારે તમે ક્લીન ડેસ્ટ્રક્ચરિંગ (destructuring) કરવા માંગતા હોવ ત્યારે તેઓ for...of લૂપ્સમાં ખૂબ ઉપયોગી થાય છે:

const fruits = ['apple', 'banana'];
for (const [index, fruit] of fruits.entries()) {
  console.log(index, fruit);
}

with() એ ભાષામાં એક નવું ઉમેરણ છે જે ચોક્કસ ઇન્ડેક્સ પરના એલિમેન્ટને બદલે છે અને એક બિલકુલ નવો એરે રિટર્ન કરે છે. તે બ્રેકેટ અસાઇનમેન્ટ—arr[index] = value—ની જેમ જ જરૂરિયાત પૂરી કરે છે, સિવાય કે તે મૂળ એરેને અસ્પૃશ્ય રાખે છે. React જેવા ફ્રેમવર્ક માં, જ્યાં ડાયરેક્ટ મ્યુટેશન ચેન્જ ડિટેક્શનને તોડી નાખે છે, ત્યાં with() એ વધુ સુરક્ષિત માર્ગ છે:

const original = [10, 20, 30];
const updated = original.with(1, 99);
// original: [10, 20, 30]
// updated:  [10, 99, 30]

Spread અને Rest: ... ની બે દિશાઓ

Spread સિન્ટેક્સ અને rest પેરામીટર બંનેમાં સમાન ત્રણ ટપકાં હોય છે, પરંતુ તેઓ ડેટાને વિરુદ્ધ દિશાઓમાં લઈ જાય છે.

એરેને વ્યક્તિગત એલિમેન્ટ્સમાં વિસ્તારવા માટે spread operator નો ઉપયોગ કરો. એરેને મર્જ કરવા અથવા તેને ક્લોન કરવા માટે આ સૌથી સ્વચ્છ રીત છે:

const front = [1, 2];
const back = [3, 4];
const merged = [...front, ...back];

const copy = [...merged];

તમે વધારાની વસ્તુઓ પણ સરળતાથી ઉમેરી શકો છો: [0, ...front, 99, ...back].

rest parameter અનેક એલિમેન્ટ્સને એક સિંગલ એરેમાં એકત્રિત કરે છે. ફંક્શન સિગ્નેચરની અંદર, તે બાકી રહેલા તમામ આર્ગ્યુમેન્ટ્સને કલેક્ટ કરે છે:

function logFirst(first, ...rest) {
  console.log('First:', first);
  console.log('Rest:', rest);
}
logFirst('a', 'b', 'c');
// First: a
// Rest: ['b', 'c']

સાથે મળીને તેઓ વેરિએડિક ઇનપુટ સ્વીકારવા, તેને એરે તરીકે પ્રોસેસ કરવા અને પછી તેને ફરીથી ફંક્શન કોલ્સ અથવા નવા ડેટા સ્ટ્રક્ચર્સમાં વિભાજિત કરવાની સામાન્ય કામગીરી સંભાળે છે.

મુખ્ય વાત

આ પદ્ધતિઓનો હેતુ વન-લાઇનર્સથી સાથીદારોને પ્રભાવિત કરવાનો નથી. તેનો હેતુ તમારા ઇરાદાને સ્પષ્ટ કરવાનો છે. જ્યારે અન્ય ડેવલપર filter() જુએ છે, ત્યારે તેઓ જાણે છે કે તમે આઇટમ્સ દૂર કરી રહ્યા છો. જ્યારે તેઓ reduce() જુએ છે, ત્યારે તેઓ જાણે છે કે તમે ડેટાને કમ્પ્રેસ (collapse) કરી રહ્યા છો. જ્યારે તેઓ with() જુએ છે, ત્યારે તેઓ જાણે છે કે મૂળ એરે ક્યાંક અકબંધ છે. આ સાધનોમાં નિપુણતા મેળવવાનો અર્થ એ છે કે એવો કોડ લખવો જે પોતાની જાતે સમજૂતી આપે છે, અને તે કોઈપણ ચતુર લૂપ કરતા ઘણો વધારે સમય બચાવશે.