മലയാളം

ജാവാസ്ക്രിപ്റ്റ് ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ മനസ്സിലാക്കുന്നതിനും നടപ്പിലാക്കുന്നതിനുമുള്ള ഒരു സമ്പൂർണ്ണ ഗൈഡ്. മികച്ച ഡാറ്റാ കൈകാര്യത്തിനായി കസ്റ്റം ഇറ്ററേറ്ററുകൾ നിർമ്മിക്കാൻ ഇത് നിങ്ങളെ സഹായിക്കും.

ജാവാസ്ക്രിപ്റ്റ് ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളിൻ്റെയും കസ്റ്റം ഇറ്ററേറ്ററുകളുടെയും രഹസ്യം ചുരുളഴിക്കുന്നു

ജാവാസ്ക്രിപ്റ്റിൻ്റെ ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ, ഡാറ്റാ സ്ട്രക്ച്ചറുകളിലൂടെ കടന്നുപോകുന്നതിന് ഒരു ഏകീകൃത മാർഗ്ഗം നൽകുന്നു. ഈ പ്രോട്ടോക്കോൾ മനസ്സിലാക്കുന്നത്, അറേകളും സ്ട്രിംഗുകളും പോലുള്ള ബിൽറ്റ്-ഇൻ ഇറ്ററബിൾസുമായി കാര്യക്ഷമമായി പ്രവർത്തിക്കാനും, നിർദ്ദിഷ്ട ഡാറ്റാ സ്ട്രക്ച്ചറുകൾക്കും ആപ്ലിക്കേഷൻ ആവശ്യകതകൾക്കും അനുസൃതമായി സ്വന്തമായി കസ്റ്റം ഇറ്ററബിൾസ് നിർമ്മിക്കാനും ഡെവലപ്പർമാരെ പ്രാപ്തരാക്കുന്നു. ഈ ഗൈഡ് ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളിനെയും കസ്റ്റം ഇറ്ററേറ്ററുകൾ എങ്ങനെ നടപ്പിലാക്കാം എന്നതിനെയും കുറിച്ച് സമഗ്രമായ ഒരു പര്യവേക്ഷണം നൽകുന്നു.

എന്താണ് ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ?

ഒരു ഒബ്ജക്റ്റിനെ എങ്ങനെ ഇറ്ററേറ്റ് ചെയ്യാം, അതായത് അതിലെ ഘടകങ്ങളെ എങ്ങനെ ക്രമമായി ആക്സസ് ചെയ്യാം എന്ന് ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ നിർവചിക്കുന്നു. ഇതിന് രണ്ട് ഭാഗങ്ങളുണ്ട്: ഇറ്ററബിൾ പ്രോട്ടോക്കോളും ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളും.

ഇറ്ററബിൾ പ്രോട്ടോക്കോൾ

ഒരു ഒബ്ജക്റ്റിന് Symbol.iterator എന്ന കീയിൽ ഒരു മെത്തേഡ് ഉണ്ടെങ്കിൽ അത് ഇറ്ററബിൾ ആയി കണക്കാക്കപ്പെടുന്നു. ഈ മെത്തേഡ് ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളിന് അനുസൃതമായ ഒരു ഒബ്ജക്റ്റ് നൽകണം.

ചുരുക്കത്തിൽ, ഒരു ഇറ്ററബിൾ ഒബ്ജക്റ്റിന് തനിക്കുവേണ്ടി ഒരു ഇറ്ററേറ്റർ എങ്ങനെ നിർമ്മിക്കണമെന്ന് അറിയാം.

ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ

ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ ഒരു ശ്രേണിയിൽ നിന്ന് എങ്ങനെ മൂല്യങ്ങൾ വീണ്ടെടുക്കാമെന്ന് നിർവചിക്കുന്നു. ഒരു ഒബ്ജക്റ്റിന് രണ്ട് പ്രോപ്പർട്ടികളുള്ള ഒരു ഒബ്ജക്റ്റ് നൽകുന്ന next() എന്ന മെത്തേഡ് ഉണ്ടെങ്കിൽ അത് ഒരു ഇറ്ററേറ്റർ ആയി കണക്കാക്കപ്പെടുന്നു:

ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളിൻ്റെ പ്രധാന ഭാഗം next() മെത്തേഡാണ്. next()-ലേക്കുള്ള ഓരോ കോളും ഇറ്ററേറ്ററിനെ മുന്നോട്ട് കൊണ്ടുപോകുകയും ശ്രേണിയിലെ അടുത്ത മൂല്യം നൽകുകയും ചെയ്യുന്നു. എല്ലാ മൂല്യങ്ങളും നൽകിക്കഴിയുമ്പോൾ, next() done true ആയി സജ്ജീകരിച്ച ഒരു ഒബ്ജക്റ്റ് നൽകുന്നു.

ബിൽറ്റ്-ഇൻ ഇറ്ററബിൾസ്

ജാവാസ്ക്രിപ്റ്റ് സഹജമായി ഇറ്ററേറ്റ് ചെയ്യാവുന്ന നിരവധി ബിൽറ്റ്-ഇൻ ഡാറ്റാ സ്ട്രക്ച്ചറുകൾ നൽകുന്നു. അവയിൽ ചിലത്:

ഈ ഇറ്ററബിൾസ് for...of ലൂപ്പ്, സ്പ്രെഡ് സിൻ്റാക്സ് (...), കൂടാതെ ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളിനെ ആശ്രയിക്കുന്ന മറ്റ് ഘടനകളിലും നേരിട്ട് ഉപയോഗിക്കാൻ കഴിയും.

അറേകൾ ഉപയോഗിച്ചുള്ള ഉദാഹരണം:


const myArray = ["apple", "banana", "cherry"];

for (const item of myArray) {
  console.log(item); // Output: apple, banana, cherry
}

സ്ട്രിംഗുകൾ ഉപയോഗിച്ചുള്ള ഉദാഹരണം:


const myString = "Hello";

for (const char of myString) {
  console.log(char); // Output: H, e, l, l, o
}

for...of ലൂപ്പ്

ഇറ്ററബിൾ ഒബ്ജക്റ്റുകളിലൂടെ ഇറ്ററേറ്റ് ചെയ്യുന്നതിനുള്ള ശക്തമായ ഒരു നിർമ്മിതിയാണ് for...of ലൂപ്പ്. ഇത് ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളിൻ്റെ സങ്കീർണ്ണതകൾ സ്വയമേവ കൈകാര്യം ചെയ്യുന്നു, ഇത് ഒരു ശ്രേണിയിലെ മൂല്യങ്ങൾ ആക്സസ് ചെയ്യുന്നത് എളുപ്പമാക്കുന്നു.

for...of ലൂപ്പിൻ്റെ സിൻ്റാക്സ് ഇതാണ്:


for (const element of iterable) {
  // ഓരോ എലമെൻ്റിനും വേണ്ടി എക്സിക്യൂട്ട് ചെയ്യേണ്ട കോഡ്
}

for...of ലൂപ്പ് ഇറ്ററബിൾ ഒബ്ജക്റ്റിൽ നിന്ന് ഇറ്ററേറ്റർ വീണ്ടെടുക്കുകയും (Symbol.iterator ഉപയോഗിച്ച്), done true ആകുന്നതുവരെ ഇറ്ററേറ്ററിൻ്റെ next() മെത്തേഡ് ആവർത്തിച്ച് വിളിക്കുകയും ചെയ്യുന്നു. ഓരോ ഇറ്ററേഷനിലും, next() നൽകുന്ന value പ്രോപ്പർട്ടി element വേരിയബിളിന് നൽകുന്നു.

കസ്റ്റം ഇറ്ററേറ്ററുകൾ നിർമ്മിക്കുന്നു

ജാവാസ്ക്രിപ്റ്റ് ബിൽറ്റ്-ഇൻ ഇറ്ററബിൾസ് നൽകുന്നുണ്ടെങ്കിലും, ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളിൻ്റെ യഥാർത്ഥ ശക്തി നിങ്ങളുടെ സ്വന്തം ഡാറ്റാ സ്ട്രക്ച്ചറുകൾക്കായി കസ്റ്റം ഇറ്ററേറ്ററുകൾ നിർവചിക്കാനുള്ള അതിൻ്റെ കഴിവിലാണ്. ഇത് നിങ്ങളുടെ ഡാറ്റ എങ്ങനെ കടന്നുപോകുന്നുവെന്നും ആക്സസ് ചെയ്യുന്നുവെന്നും നിയന്ത്രിക്കാൻ നിങ്ങളെ അനുവദിക്കുന്നു.

ഒരു കസ്റ്റം ഇറ്ററേറ്റർ എങ്ങനെ നിർമ്മിക്കാം എന്നത് താഴെ നൽകുന്നു:

  1. നിങ്ങളുടെ കസ്റ്റം ഡാറ്റാ സ്ട്രക്ച്ചറിനെ പ്രതിനിധീകരിക്കുന്ന ഒരു ക്ലാസ് അല്ലെങ്കിൽ ഒബ്ജക്റ്റ് നിർവചിക്കുക.
  2. നിങ്ങളുടെ ക്ലാസിലോ ഒബ്ജക്റ്റിലോ Symbol.iterator മെത്തേഡ് നടപ്പിലാക്കുക. ഈ മെത്തേഡ് ഒരു ഇറ്ററേറ്റർ ഒബ്ജക്റ്റ് നൽകണം.
  3. ഇറ്ററേറ്റർ ഒബ്ജക്റ്റിന് value, done പ്രോപ്പർട്ടികളുള്ള ഒരു ഒബ്ജക്റ്റ് നൽകുന്ന next() മെത്തേഡ് ഉണ്ടായിരിക്കണം.

ഉദാഹരണം: ഒരു ലളിതമായ റേഞ്ചിനായി ഒരു ഇറ്ററേറ്റർ നിർമ്മിക്കുന്നു

നമുക്ക് ഒരു സംഖ്യകളുടെ ശ്രേണിയെ പ്രതിനിധീകരിക്കുന്ന Range എന്നൊരു ക്ലാസ് ഉണ്ടാക്കാം. ഈ ശ്രേണിയിലെ സംഖ്യകളിലൂടെ ഇറ്ററേറ്റ് ചെയ്യാൻ നമ്മൾ ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ നടപ്പിലാക്കും.


class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }

  [Symbol.iterator]() {
    let currentValue = this.start;
    const that = this; // Capture 'this' for use inside the iterator object

    return {
      next() {
        if (currentValue <= that.end) {
          return {
            value: currentValue++,
            done: false,
          };
        } else {
          return {
            value: undefined,
            done: true,
          };
        }
      },
    };
  }
}

const myRange = new Range(1, 5);

for (const number of myRange) {
  console.log(number); // Output: 1, 2, 3, 4, 5
}

വിശദീകരണം:

ഉദാഹരണം: ഒരു ലിങ്ക്ഡ് ലിസ്റ്റിനായി ഒരു ഇറ്ററേറ്റർ നിർമ്മിക്കുന്നു

നമുക്ക് മറ്റൊരു ഉദാഹരണം പരിഗണിക്കാം: ഒരു ലിങ്ക്ഡ് ലിസ്റ്റ് ഡാറ്റാ സ്ട്രക്ച്ചറിനായി ഒരു ഇറ്ററേറ്റർ നിർമ്മിക്കുന്നത്. ഒരു ലിങ്ക്ഡ് ലിസ്റ്റ് എന്നത് നോഡുകളുടെ ഒരു ശ്രേണിയാണ്, ഇവിടെ ഓരോ നോഡിലും ഒരു മൂല്യവും ലിസ്റ്റിലെ അടുത്ത നോഡിലേക്കുള്ള ഒരു റെഫറൻസും (പോയിൻ്റർ) അടങ്ങിയിരിക്കുന്നു. ലിസ്റ്റിലെ അവസാന നോഡിന് null (അല്ലെങ്കിൽ undefined) ലേക്ക് ഒരു റെഫറൻസുണ്ടായിരിക്കും.


class LinkedListNode {
    constructor(value, next = null) {
        this.value = value;
        this.next = next;
    }
}

class LinkedList {
    constructor() {
        this.head = null;
    }

    append(value) {
        const newNode = new LinkedListNode(value);
        if (!this.head) {
            this.head = newNode;
            return;
        }

        let current = this.head;
        while (current.next) {
            current = current.next;
        }
        current.next = newNode;
    }

    [Symbol.iterator]() {
        let current = this.head;

        return {
            next() {
                if (current) {
                    const value = current.value;
                    current = current.next;
                    return {
                        value: value,
                        done: false
                    };
                } else {
                    return {
                        value: undefined,
                        done: true
                    };
                }
            }
        };
    }
}

// Example Usage:
const myList = new LinkedList();
myList.append("London");
myList.append("Paris");
myList.append("Tokyo");

for (const city of myList) {
    console.log(city); // Output: London, Paris, Tokyo
}

വിശദീകരണം:

ജനറേറ്റർ ഫംഗ്ഷനുകൾ

ജനറേറ്റർ ഫംഗ്ഷനുകൾ ഇറ്ററേറ്ററുകൾ നിർമ്മിക്കുന്നതിന് കൂടുതൽ സംക്ഷിപ്തവും ലളിതവുമായ ഒരു മാർഗ്ഗം നൽകുന്നു. ആവശ്യാനുസരണം മൂല്യങ്ങൾ ഉത്പാദിപ്പിക്കാൻ അവ yield കീവേഡ് ഉപയോഗിക്കുന്നു.

function* സിൻ്റാക്സ് ഉപയോഗിച്ചാണ് ഒരു ജനറേറ്റർ ഫംഗ്ഷൻ നിർവചിക്കുന്നത്.

ഉദാഹരണം: ഒരു ജനറേറ്റർ ഫംഗ്ഷൻ ഉപയോഗിച്ച് ഒരു ഇറ്ററേറ്റർ നിർമ്മിക്കുന്നു

നമുക്ക് Range ഇറ്ററേറ്റർ ഒരു ജനറേറ്റർ ഫംഗ്ഷൻ ഉപയോഗിച്ച് മാറ്റിയെഴുതാം:


class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }

  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i++) {
      yield i;
    }
  }
}

const myRange = new Range(1, 5);

for (const number of myRange) {
  console.log(number); // Output: 1, 2, 3, 4, 5
}

വിശദീകരണം:

ജനറേറ്റർ ഫംഗ്ഷനുകൾ next() മെത്തേഡും done ഫ്ലാഗും സ്വയമേവ കൈകാര്യം ചെയ്യുന്നതിലൂടെ ഇറ്ററേറ്റർ നിർമ്മാണം ലളിതമാക്കുന്നു.

ഉദാഹരണം: ഫിബൊനാച്ചി സീക്വൻസ് ജനറേറ്റർ

ജനറേറ്റർ ഫംഗ്ഷനുകൾ ഉപയോഗിക്കുന്നതിൻ്റെ മറ്റൊരു മികച്ച ഉദാഹരണമാണ് ഫിബൊനാച്ചി സീക്വൻസ് ഉണ്ടാക്കുന്നത്:


function* fibonacciSequence() {
  let a = 0;
  let b = 1;

  while (true) {
    yield a;
    [a, b] = [b, a + b]; // Destructuring assignment for simultaneous update
  }
}

const fibonacci = fibonacciSequence();

for (let i = 0; i < 10; i++) {
  console.log(fibonacci.next().value); // Output: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
}

വിശദീകരണം:

ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ ഉപയോഗിക്കുന്നതിൻ്റെ പ്രയോജനങ്ങൾ

അഡ്വാൻസ്ഡ് ഇറ്ററേറ്റർ ടെക്നിക്കുകൾ

ഇറ്ററേറ്ററുകൾ സംയോജിപ്പിക്കുന്നു

നിങ്ങൾക്ക് ഒന്നിലധികം ഇറ്ററേറ്ററുകളെ ഒരൊറ്റ ഇറ്ററേറ്ററിലേക്ക് സംയോജിപ്പിക്കാൻ കഴിയും. ഒന്നിലധികം ഉറവിടങ്ങളിൽ നിന്നുള്ള ഡാറ്റ ഏകീകൃതമായി പ്രോസസ്സ് ചെയ്യേണ്ടിവരുമ്പോൾ ഇത് ഉപയോഗപ്രദമാണ്.


function* combineIterators(...iterables) {
  for (const iterable of iterables) {
    for (const item of iterable) {
      yield item;
    }
  }
}

const array1 = [1, 2, 3];
const array2 = ["a", "b", "c"];
const string1 = "XYZ";

const combined = combineIterators(array1, array2, string1);

for (const value of combined) {
  console.log(value); // Output: 1, 2, 3, a, b, c, X, Y, Z
}

ഈ ഉദാഹരണത്തിൽ, `combineIterators` ഫംഗ്ഷൻ എത്ര ഇറ്ററബിൾസിനെയും ആർഗ്യുമെൻ്റായി എടുക്കുന്നു. ഇത് ഓരോ ഇറ്ററബിളിലൂടെയും കടന്നുപോകുകയും ഓരോ ഐറ്റവും യീൽഡ് ചെയ്യുകയും ചെയ്യുന്നു. ഇൻപുട്ട് ഇറ്ററബിൾസിലെ എല്ലാ മൂല്യങ്ങളും നൽകുന്ന ഒരൊറ്റ ഇറ്ററേറ്ററാണ് ഫലം.

ഇറ്ററേറ്ററുകൾ ഫിൽട്ടർ ചെയ്യലും രൂപാന്തരപ്പെടുത്തലും

മറ്റൊരു ഇറ്ററേറ്റർ ഉത്പാദിപ്പിക്കുന്ന മൂല്യങ്ങളെ ഫിൽട്ടർ ചെയ്യുകയോ രൂപാന്തരപ്പെടുത്തുകയോ ചെയ്യുന്ന ഇറ്ററേറ്ററുകളും നിങ്ങൾക്ക് സൃഷ്ടിക്കാൻ കഴിയും. ഇത് ഒരു പൈപ്പ്ലൈനിൽ ഡാറ്റ പ്രോസസ്സ് ചെയ്യാനും, ഓരോ മൂല്യവും ഉത്പാദിപ്പിക്കപ്പെടുമ്പോൾ അതിൽ വ്യത്യസ്ത ഓപ്പറേഷനുകൾ പ്രയോഗിക്കാനും നിങ്ങളെ അനുവദിക്കുന്നു.


function* filterIterator(iterable, predicate) {
  for (const item of iterable) {
    if (predicate(item)) {
      yield item;
    }
  }
}

function* mapIterator(iterable, transform) {
  for (const item of iterable) {
    yield transform(item);
    }
}

const numbers = [1, 2, 3, 4, 5, 6];

const evenNumbers = filterIterator(numbers, (x) => x % 2 === 0);
const squaredEvenNumbers = mapIterator(evenNumbers, (x) => x * x);

for (const value of squaredEvenNumbers) {
    console.log(value); // Output: 4, 16, 36
}

ഇവിടെ, `filterIterator` ഒരു ഇറ്ററബിളും ഒരു പ്രെഡിക്കേറ്റ് ഫംഗ്ഷനും എടുക്കുന്നു. പ്രെഡിക്കേറ്റ് `true` നൽകുന്ന ഐറ്റംസ് മാത്രമേ ഇത് യീൽഡ് ചെയ്യുകയുള്ളൂ. `mapIterator` ഒരു ഇറ്ററബിളും ഒരു ട്രാൻസ്ഫോം ഫംഗ്ഷനും എടുക്കുന്നു. ഓരോ ഐറ്റത്തിലും ട്രാൻസ്ഫോം ഫംഗ്ഷൻ പ്രയോഗിച്ചതിൻ്റെ ഫലം ഇത് യീൽഡ് ചെയ്യുന്നു.

യഥാർത്ഥ ലോകത്തിലെ പ്രയോഗങ്ങൾ

ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ ജാവാസ്ക്രിപ്റ്റ് ലൈബ്രറികളിലും ഫ്രെയിംവർക്കുകളിലും വ്യാപകമായി ഉപയോഗിക്കപ്പെടുന്നു, കൂടാതെ വിവിധ യഥാർത്ഥ ലോക പ്രയോഗങ്ങളിലും ഇത് വിലപ്പെട്ടതാണ്, പ്രത്യേകിച്ചും വലിയ ഡാറ്റാസെറ്റുകളുമായോ അസിൻക്രണസ് ഓപ്പറേഷനുകളുമായോ ഇടപെടുമ്പോൾ.

മികച്ച രീതികൾ

ഉപസംഹാരം

ജാവാസ്ക്രിപ്റ്റ് ഇറ്ററേറ്റർ പ്രോട്ടോക്കോൾ ഡാറ്റാ സ്ട്രക്ച്ചറുകളിലൂടെ കടന്നുപോകാൻ ശക്തവും വഴക്കമുള്ളതുമായ ഒരു മാർഗ്ഗം നൽകുന്നു. ഇറ്ററബിൾ, ഇറ്ററേറ്റർ പ്രോട്ടോക്കോളുകൾ മനസ്സിലാക്കുന്നതിലൂടെയും ജനറേറ്റർ ഫംഗ്ഷനുകൾ പ്രയോജനപ്പെടുത്തുന്നതിലൂടെയും നിങ്ങളുടെ നിർദ്ദിഷ്ട ആവശ്യങ്ങൾക്കനുസരിച്ച് കസ്റ്റം ഇറ്ററേറ്ററുകൾ നിർമ്മിക്കാൻ നിങ്ങൾക്ക് കഴിയും. ഇത് ഡാറ്റയുമായി കാര്യക്ഷമമായി പ്രവർത്തിക്കാനും കോഡിൻ്റെ വായനാക്ഷമത മെച്ചപ്പെടുത്താനും നിങ്ങളുടെ ആപ്ലിക്കേഷനുകളുടെ പ്രകടനം വർദ്ധിപ്പിക്കാനും നിങ്ങളെ അനുവദിക്കുന്നു. ഇറ്ററേറ്ററുകളിൽ വൈദഗ്ദ്ധ്യം നേടുന്നത് ജാവാസ്ക്രിപ്റ്റിൻ്റെ കഴിവുകളെക്കുറിച്ചുള്ള ആഴത്തിലുള്ള ധാരണ നൽകുകയും കൂടുതൽ ലളിതവും കാര്യക്ഷമവുമായ കോഡ് എഴുതാൻ നിങ്ങളെ പ്രാപ്തരാക്കുകയും ചെയ്യുന്നു.