1

My data structure (condensed)

const data = {
  "cars": [
    {
      "name": "toyota",
      "sedan": {
        "2d": [
          {
            "name": "corolla",
            "year": 2020
          },
          {
            "name": "supra",
            "year": 1986
          }
        ]
      }
    }
  ]
};

To find the object by name, I would do:

const twoDcars = data.cars.reduce(car => car.sedan);
const match = twoDcars.sedan['2d'].filter(car => car.name === "corolla");
console.log(match); //[ { name: 'corolla', year: 2020 } ]

With conditional check:

const twoDcars = data.cars && data.cars.reduce(car => car.sedan);
const match = twoDcars && twoDcars.sedan && twoDcars.sedan['2d'] && twoDcars.sedan['2d'].filter(car => car && car.name === "corolla");
console.log(match);  //[ { name: 'corolla', year: 2020 } ]

With try/catch:

let match;
try {
  match = data.cars.reduce(car => car.sedan).sedan['2d'].filter(car => car.name === "corolla");
} catch {}
console.log(match); //[ { name: 'corolla', year: 2020 } ]

My question is, what is the preferred/industry-standard of doing this.

  • A && A.B && A.B.C && A.B.C.D
  • try {A.B.C.D} catch{}
  • Some other approach?

My requirement is pretty simple.

  • Find the match if possible
  • App shouldn't break on any conditions.

What am I trying to do is avoid hundreds of && or `try{}catch{}`` everywhere in my code. I can create utility methods whenever possible but with the complex/nested data that I'm dealing with, it is often impossible.

WhatisSober
  • 988
  • 2
  • 12
  • 32
  • Trying with an empty catch is almost certainly not what you want. – ChiefTwoPencils Oct 26 '19 at 20:57
  • @ChiefTwoPencils What is the downside? Upside is any exception will not break my app-flow. – WhatisSober Oct 26 '19 at 21:01
  • Downside is you won't even know it happened or why. See [this](https://stackoverflow.com/questions/1234343/why-are-empty-catch-blocks-a-bad-idea). – ChiefTwoPencils Oct 26 '19 at 21:03
  • @ChiefTwoPencils That is exactly my question. I do not care if my search fails just app shouldn't break. – WhatisSober Oct 26 '19 at 21:04
  • I know, but you also asked for industry standard; that's not standard and seen as a smell more often than not. The issue of avoiding hundreds of this or that is different than using a construct in a non-standard way. For example, why not put it in one place and call it a hundred times? – ChiefTwoPencils Oct 26 '19 at 21:07
  • please add **how** you would like to access the given data structure? – Nina Scholz Oct 26 '19 at 21:08
  • 1
    `reduce(car => car.sedan)` doesn't make sense. Did you mean `find` or still something else? – trincot Oct 26 '19 at 21:09
  • @trincot My bad. That was meant to be `find`. – WhatisSober Oct 26 '19 at 21:45
  • Curious if this data can be restructured because you're correct, this is an issue caused by deeply nested types; but it doesn't have to be that way. – ChiefTwoPencils Oct 26 '19 at 22:16
  • 1
    @ChiefTwoPencils That is the problem. I am currently debating between transforming the data before it can be used vs using the raw data. – WhatisSober Oct 26 '19 at 22:18

2 Answers2

1

If possible, I would probably do some massaging of the raw data to get it in a form where you can filter down at the top level and ensure you're not dealing with all the possible nulls everywhere in you code. I'd also get rid of the check on cars by ensuring there's always an empty list of cars. That way, filter and the rest will just work.

I would probably shoot to flatten the car objects into individual cars with all the props; like so:

const data = {
  "cars": [
    {
      "year": 2020,
      "make": "toyota",
      "model": "corolla",
      "type": "sedan",
      "doors" : 2
    },
    {
      "year": 1986,
      "make": "toyota",
      "model": "supra",
      "type": "sedan",
      "doors" : 2
    }
  ]
};

I wouldn't use multiple chained filters for this I'm just showing how much easier it would be to filter more directly and get all sedans, two-door sedans, etc. simplifying your code and life :)

let results = data
  .cars
  .filter(car => car.type === 'sedan')     // all sedans
  .filter(car => car.doors === 2)          // two-door sedans
  .filter(car => car.model === 'corolla'); // two-door corollas

Of course, once you massage it, you can reorder the filters to be more direct; like so (assuming you know a corolla is a sedan and you want only two-door models):

let results = data
  .cars
  .filter(car => car.model === 'corolla' && car.doors === 2);
ChiefTwoPencils
  • 13,548
  • 8
  • 49
  • 75
0

Whether to use try/catch or to add in the guarding conditions, is a matter of opinion, although I have seen more often the guarded expressions.

But there is no doubt that we're all going to be fans of the conditional chaining feature (also: mdn), currently in stage 3.

Then your code would look like:

const match = data.cars?.find(car => car.sedan)
              ?.sedan?.['2d']?.filter(car => car?.name === "corolla");

If searches in a nested object are frequent, then you could consider to flatten the structure into an array of non-nested objects.

To avoid a scan of the whole array, you could sort that array by one of its object-properties, allowing for a binary search. You can even add some Map objects as separate ways to access the same data, but by key. This obviously brings you back to more nesting, but it would be an additional structure (not replacing the array) for drilling down into your data faster than by filtering the whole lot. Such a Map would hold per key an array of matching objects (no copies, but the same object references as in the main array).

trincot
  • 317,000
  • 35
  • 244
  • 286
  • I'll certainly be a fan of the feature but not of this usage. While it works and is more compact, needing to use it like in the example seems to be a symptom of poor decisions elsewhere IMHO. – ChiefTwoPencils Oct 26 '19 at 21:35