Search code examples
javascripttypescriptobject

Iterate through all nested children of an object and create a flat array with a value if type is equal to a certain string


I know there are a lot of questions and articles about this but I can't wrap my head around this and nothing quite helped me.

export type CarId = string;
export type CarType = 'sedan' | 'suv' | 'wagon';
export type Car = {
  id: CarId;
  type: CarType;
  options?: Record<string, string | number | boolean | string[] | null>;
  models?: Car[];
}

I have a data like below: (think that the object is a lot bigger and so much more complex in terms of nesting and 5-10 suv type cars)

export const cars: Car = {
  type: 'sedan',
  id: 'a',
  options: {},
  models: [
    {
      type:'sedan',
      id: 'b',
      options: {},
      models: []
    },
    {
      type:'wagon',
      id: 'c',
      options: {},
      models: [
        {
          type:'wagon',
          id: 'd',
          options: {},
          models: [
              type:'suv',
              id: 'e',
              options: {
                carUrl: 'https://audi.com/a4',
                ...
              },
              models: []
          ]
        },
        {...}
      ]
    },
  ]

}

What I wanna do is to write a function that will give me a flat array of carURLs if the type of the car is a suv.

I have tried everything and I have something like this now since I removed a lot of the code that had maybe some potential but not going anywhere:

export iterate = (car: Car) => {
   let urlArr = [];
   let keys = Object.keys(car);
   keys.forEach((key) => {
      if(car[key] === 'type' && (Object.values(car[key])) === 'suv') {
         urlArr.push(/*<need to push the url somehow>*/);
      }
   })
   return urlArr;
   
}

and with deep nesting I need all the URLs

console.log(urlArr) should be something like ['https://audi.com/a4', 'https://bmw.com/m3', 'https://toyota.com/corolla' ...]

can someone please help me with the function?

Thank you in advance!


Solution

  • Using recursion you can do this, here is what I did-

    1. First, I checked if the root object is of type "suv" and has any car URL, then push it. (you will notice that the root object is of type, "sedan" so its car URL will be ignored. You can try changing its type to "suv".)
    2. Second, I run a loop on the root object's models array and find the car URLs of the "suv" type only.
    3. Then, inside the same function, I checked if the current model also has any nested models, then loop on them as well and find the car URLs.

    So, here is the working demo-

    const carsDataObj = {
      type: "sedan",
      id: "a",
      options: {
        carUrl: "https://Seat.com/a4"
      },
      models: [
        {
          type: "suv",
          id: "b",
          options: {
            carUrl: "https://Lamborghini.com/a4"
          },
          models: []
        },
        {
          type: "suv",
          id: "b",
          options: {
            carUrl: "https://Jeep.com/a4"
          },
          models: []
        },
        {
          type: "wagon",
          id: "c",
          options: {
            carUrl: "https://F-150 Raptor.com/a4"
          },
          models: [
            {
              type: "wagon",
              id: "d",
              options: {},
              models: [
                {
                  type: "suv",
                  id: "e",
                  options: {
                    carUrl: "https://Ford.com/a4"
                  },
                  models: [
                    {
                      type: "suv",
                      id: "e",
                      options: {
                        carUrl: "https://audi.com/a4"
                      },
                      models: []
                    }
                  ]
                }
              ]
            },
            {
              type: "suv",
              id: "d",
              options: {
                carUrl: "https://rolls.com/a4"
              },
              models: [
                {
                  type: "suv",
                  id: "e",
                  options: {
                    carUrl: "https://Ferrari.com/a4"
                  },
                  models: []
                }
              ]
            }
          ]
        }
      ]
    };
    
    let result = [];
    
    function checkIfSUVThenPush(payload) {
      if (payload.type == "suv" && payload.options && payload.options.carUrl) {
        result.push(payload.options.carUrl);
      }
    }
    
    function loopOnModels(arr) {
      arr.forEach((item) => {
        // check if item is suv and has any car
        checkIfSUVThenPush(item);
        // Now, if item has any models then use recursion and call same method
        if (item.models && item.models.length) {
          loopOnModels(item.models);
        }
      });
      return result;
    }
    
    
    // First, check on root object if any car url available
    checkIfSUVThenPush(carsDataObj);
    
    // Now, loop on root object's models array
    let urls = loopOnModels(carsDataObj.models);
    console.log(urls);

    Let me know if it works.