Home > Enterprise >  Iterate through all nested children of an object and create a flat array with a value if type is equ
Iterate through all nested children of an object and create a flat array with a value if type is equ

Time:12-22

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!

CodePudding user response:

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.

CodePudding user response:

You could go with a recursive function that will check each car then call itself on all cars in the models object :

function findSUVURLs(car:Car):Array<string> {
    const urlsArray:Array<string> = [];
    
    //First checking if current car is an suv and getting the url if there is one
    if (car.type === "suv" && car.options.carUrl) {
        urlsArray.push(car.options.carUrl);
    }
    
    //Then calling the same function on all cars in the 'models' property, pushing them to the same array
    car.models.forEach(subCar=>{
        //The spread operator '...' ensures we "flatten" each array and get all urls from nested objects
        urlsArray.push(...findSUVURLs(subCar));
    });
    
    return urlsArray;
}

CodePudding user response:

You could try starting by flattening the object into an array of Car objects without the nested models, then reducing it to an array of URLs.

const flatten = (a: Car) => {
    if (Array.isArray(a)) return a.reduce((prev, curr) => {
      // Add the current car without models
      prev.push({...curr, models: []});
      // Check if there are nested models and flatten those too
      const nestedFlat = curr.models?.length ? flatten(curr.models) : [];
      prev.push(...nestedFlat);
      return prev;
   }, []);
   return [{...a, models: []}, ...flatten(a.models)];
}
  • Related