Home > Mobile >  Alternative to labeled loops
Alternative to labeled loops

Time:12-07

I'm adding objects to a collection inside a for-loop, like this (points is a Point[]):

for (const point of points) {
    // do and check some stuff
    collection.push(point)
}

I then realized I needed objects to be unique, but since they're of a custom type, I can't use a Set<Point>, which doesn't use the objects' equals function to compare them. My naive approach was checking each of the already assigned objects, which is fine performance-wise since the collection is usually very small:

addPoints: for (const point of points) {
    // do and check some stuff
    for (const existingPoint of collection) {
        if (point.equals(existingPoint)) break addPoints
    }
    collection.push(point)
}

As you can see, I'm using the break label construct here in order to break out of my outer loop in case I encounter a point I've already added to the collection.

My linter doesn't like this, throwing a "no-labels" violation, whose description states

While convenient in some cases, labels tend to be used only rarely and are frowned upon by some as a remedial form of flow control that is more error prone and harder to understand.

My question is: How can I handle this use case without adding unnecessary complexity?

Things I've considered, but dislike because they add lots of code that feels unnecessarily clunky compared to the label:

  • Moving the check into a separate function, which is essentially the inner loop, then call that with a simple if - break in my outer loop.
  • Setting a flag in the inner loop, then checking that in the outer loop in order to break out.
  • Writing a whole wrapper class for my collection that uses equals to check for unique members.

CodePudding user response:

How about something like this:

for (const point of points) {
  const alreadyExists = collection.some(
    (p) => p.x === point.x && p.y === point.y
  );

  if (!alreadyExists) {
    collection.push(point);
  }
}

With some function you can write your own comparator and check the object equality with any rule you need

CodePudding user response:

I usually use a Map for things like this, and define the map keys as e.g. strings that make out unique elements:

const map = new Map<string, Point>();

for (const point of points) {
    const key = ...
    map.set(key, point);
}

const collection = [...map.values()];

This requires that you can build a key from the point attributes that uniquely define a point. You didn't include your Point interface, but if it's a point in space and has x and y properties, you could e.g. do map.set([point.x, point.y].join(';'), point), map.set(JSON.stringify(point), point) or whatever suits your needs.

  • Related