Jak rekurzivně procházet objektem pomocí JavaScriptu

Jak napsat funkci, která hledá konkrétní pár klíč/hodnota na objektu a volat tuto funkci rekurzivně, aby procházela objekty libovolné hloubky.

Začínáme

Pro tento tutoriál vytvoříme jednoduchý projekt Node.js s jedním souborem. V počítači vyberte vhodné umístění souboru (např. složku projektů) a vytvořte soubor s názvem index.js .

Dále se ujistěte, že jste do počítače nainstalovali Node.js. Zatímco kód, který píšeme, ne závisí na tom, aby fungoval Node.js, budeme ho potřebovat ke spuštění nebo spuštění kódu, který napíšeme do index.js .

Jakmile vytvoříte svůj soubor a nainstalujete Node.js, jsme připraveni začít.

Vytvoření funkce pro spárování objektů podle klíče a hodnoty

Snadný způsob, jak pochopit pojem rekurze, je představit si točité schodiště v domě. Abyste mohli přejít z horní části schodiště dolů, musíte sejít po jednom schodu.

I když to děláte automaticky, technicky máte ve svém mozku „funkci“, která vám říká, jak sejít krok po kroku, dokud nedosáhnete dna. Tuto „funkci“ voláte pro každý schod na schodišti, dokud už žádné schody nebudou. Když jdete dolů, říkáte "funkci", aby se znovu zavolala, pokud je krok po aktuálním kroku.

Takto funguje rekurze v JavaScriptu (nebo v jakémkoli programovacím jazyce). Napíšete funkci, která provede úlohu a necháte tuto funkci zavolat znovu, pokud nesplnila nějaký požadavek – například nalezení vnořené hodnoty nebo dosažení konce seznamu.

Pro tento tutoriál napíšeme funkci, která se zaměřuje na první:nalezení vnořeného objektu. Přesněji řečeno, chceme napsat rekurzivní funkci, která najde vnořený objekt obsahující konkrétní klíč s konkrétní hodnotou.

Nejprve vytvoříme naši základní funkci a vysvětlíme, o co jde:

/index.js

const findNestedObject = (object = {}, keyToMatch = "", valueToMatch = "") => {
  // We'll implement our function here...
};

Naše funkce bude mít tři argumenty:object k procházení keyToMatch v tomto objektu a valueToMatch v rámci tohoto objektu.

/index.js

const isObject = (value) => {
  return !!(value && typeof value === "object" && !Array.isArray(value));
};

const findNestedObject = (object = {}, keyToMatch = "", valueToMatch = "") => {
  if (isObject(object)) {
    // We'll work on finding our nested object here...
  }

  return null;
};

Dále, abychom se vyhnuli chybám při běhu, v těle našeho findNestedObject funkci, přidáme if příkaz s voláním nové funkce, kterou jsme přidali výše isObject() , předáním object argument, který byl předán do findNestedObject .

Podívejte se na isObject() , chceme si být jisti, že objekt, kterým procházíme, je ve skutečnosti objekt. Abychom to zjistili, musíme ověřit, že jste předali value není null nebo nedefinováno, má typeof "objekt" a není pole. Ten poslední může vypadat divně. Musíme udělat !Array.isArray() protože v JavaScriptu Array s mají typeof "object" (což znamená, že naše předchozí typeof value === "object" test může být "oklamán" předávaným polem).

Za předpokladu, že isObject() vrátí true pro hodnotu, kterou jsme jí předali, můžeme začít objekt procházet. Pokud ne, jako záložní řešení z našeho findNestedObject() vrátíme null na znamení, že neudělali najít shodu.

/index.js

const isObject = (value) => {
  return !!(value && typeof value === "object" && !Array.isArray(value));
};

const findNestedObject = (object = {}, keyToMatch = "", valueToMatch = "") => {
  if (isObject(object)) {
    const entries = Object.entries(object);

    for (let i = 0; i < entries.length; i += 1) {
      const [treeKey, treeValue] = entries[i];

      if (treeKey === keyToMatch && treeValue === valueToMatch) {
        return object;
      }
    }
  }

  return null;
};

Když přidáme nějakou složitost, chceme nyní začít procházet naším objektem. „Přejížděním“ rozumíme smyčkování přes každý pár klíč/hodnota na object předán na findNestedObject() .

K provedení této smyčky nejprve zavoláme Object.entries() předáním našeho object . To nám vrátí pole polí, kde každé pole obsahuje key páru klíč/hodnota, který je v současné době ve smyčce zacyklen jako první prvek a value páru klíč/hodnota, který je v současné době zacyklen jako druhý prvek. Takhle:

const example = {
  first: 'thing',
  second: 'stuff',
  third: 'value',
};

Object.entries(example);

[
  ['first', 'thing'],
  ['second', 'stuff'],
  ['third', 'value']
]

Dále s naším polem párů klíč/hodnota (položky) přidáme for smyčka pro iteraci přes pole. Zde i se bude rovnat indexu aktuálního páru klíč/hodnota, přes který procházíme smyčkou. Chceme to udělat, dokud neprovedeme smyčku přes všechny celky, takže řekneme „spustit tuto smyčku, zatímco i < entries.length a pro každou iteraci a 1 na aktuální index i ."

Uvnitř for smyčky, používáme destrukci pole JavaScript pro přístup k aktuálnímu poli párů klíč/hodnota (označené entries[i] ), přičemž každému přiřadíte proměnnou. Zde přiřadíme první prvek proměnné objectKey a druhý prvek do proměnné objectValue .

Pamatujte:naším cílem je najít objekt podle předané keyToMatch a valueToMatch . Abychom našli shodu, musíme zkontrolovat každý klíč a hodnotu na našem object abychom viděli, jestli se shodují. Zde, za předpokladu, že najdeme shodu, vrátíme object protože splňuje požadavek mít keyToMatch a valueToMatch .

Přidání rekurze k procházení objektů libovolné hloubky

Nyní k té zábavnější části. Právě teď může naše funkce provádět smyčku pouze přes jednoúrovňový hloubkový objekt. To je skvělé, ale nezapomeňte, že chceme hledat vnořené objekt. Protože nevíme, kde by tento objekt mohl být ve „stromu“ (přezdívka, kterou občas uslyšíte pro objekt vnořených objektů), musíme být schopni „pokračovat“, pokud jedna z hodnot v klíči/ pár hodnot je sám o sobě objektem.

Zde přichází na řadu naše rekurze.

/index.js

const isObject = (value) => {
  return !!(value && typeof value === "object" && !Array.isArray(value));
};

const findNestedObject = (object = {}, keyToMatch = "", valueToMatch = "") => {
  if (isObject(object)) {
    const entries = Object.entries(object);

    for (let i = 0; i < entries.length; i += 1) {
      const [objectKey, objectValue] = entries[i];

      if (objectKey === keyToMatch && objectValue === valueToMatch) {
        return object;
      }

      if (isObject(objectValue)) {
        const child = findNestedObject(objectValue, keyToMatch, valueToMatch);

        if (child !== null) {
          return child;
        }
      }
    }
  }

  return null;
};

Vzpomeňte si na naši analogii se schodištěm z dřívějška. V tuto chvíli jsme sešli pouze o jeden krok. Abychom mohli přejít k dalšímu kroku, musíme naší funkci říci, aby se znovu zavolala.

V tomto případě víme, že existuje další „krok“ nebo objekt, který je třeba překonat, pokud projdete objectValue na isObject() funkce, kterou jsme nastavili dříve, vrací true . Pokud ano , to znamená, že musíme zkontrolovat, zda to objekt obsahuje keyToMatch a valueToMatch hledáme.

Abychom tímto objektem prošli, rekurzivně (to znamená, že znovu zavoláme funkci, ve které se právě nacházíme), předáme objectValue spolu s původním keyToMatch a keyToValue (to, co hledáme, se nezměnilo, jen objekt, na který se chceme podívat).

Pokud naše rekurzivní volání najde shodu (což znamená naše rekurzivní volání na findNestedObject() není vrátí null ), vrátíme tento objekt child . Za předpokladu, že naše rekurzivní volání findNestedObject() nevrátil zápas, náš průjezd by se zastavil. Pokud by naše dítě samo mělo vnořené objekty (podle naší analogie, další "krok" k sestupu), opět bychom zavolali findNestedObject() .

Protože je tento kód rekurzivní, poběží, dokud buď nenajde odpovídající objekt, nebo nevyčerpá dostupné vnořené objekty pro hledání.

Nyní na zkoušku. Zkusme najít objekt v tomto stromu s name pole rovné "Tady dole!"

/index.js

const isObject = (value) => {
  return !!(value && typeof value === "object" && !Array.isArray(value));
};

const findNestedObject = (object = {}, keyToMatch = "", valueToMatch = "") => {
  if (isObject(object)) {
    const entries = Object.entries(object);

    for (let i = 0; i < entries.length; i += 1) {
      const [objectKey, objectValue] = entries[i];

      if (objectKey === keyToMatch && objectValue === valueToMatch) {
        return object;
      }

      if (isObject(objectValue)) {
        const child = findNestedObject(objectValue, keyToMatch, valueToMatch);

        if (child !== null) {
          return child;
        }
      }
    }
  }

  return null;
};

const staircase = {
  step: 5,
  nextStep: {
    step: 4,
    nextStep: {
      step: 3,
      nextStep: {
        step: 2,
        nextStep: {
          name: "Down here!",
          step: 1,
        },
      },
    },
  },
};

const match = findNestedObject(staircase, "name", "Down here!");
console.log(match);
// { name: "Down here!", step: 1 }

const match2 = findNestedObject(staircase, "step", 3);
console.log(match2);
// { step: 3, nextStep: { step: 2, nextStep: { name: "Down here!", step: 1 } } }

Zde je rychlá ukázka tohoto běhu v reálném čase:

Zabalení

V tomto tutoriálu jsme se naučili, jak rekurzivně procházet objektem pomocí JavaScriptu. Naučili jsme se, jak vytvořit základní funkci, která byla schopna procházet klíči objektu, který jsme jí předali, a hledat odpovídající pár klíč a hodnota. Potom jsme se naučili, jak tuto funkci používat rekurzivně , volá jej zevnitř, pokud hodnota páru klíč/hodnota, přes který jsme aktuálně procházeli, byl objekt.