Typescript: Avoid comparison by reference

20,301

Solution 1

You could wrap the collection in a PointList that only allows Point objects to be added via an add method, which checks to ensure no duplicates are added.

This has the benefit of encapsulating the "No duplicates" rule in a single place, rather than hoping that all calling code will check before adding a duplicate, which would duplicate the rule in many places.

class Point {
    constructor(public x: number, public y: number) {
    }
}

class PointList {
    private points: Point[] = [];

    get length() {
        return this.points.length;
    }

    add(point: Point) {
        if (this.exists(point)) {
            // throw 'Duplicate point';
            return false;
        }

        this.points.push(point);
        return true;
    }

    exists(point: Point) {
        return this.findIndex(point) > -1;
    }

    findIndex(point: Point) {
        for (var i = 0; i < this.points.length; i++) {
            var existingPoint = this.points[i];
            if (existingPoint.x === point.x && existingPoint.y === point.y) {
                return i;
            }
        }

        return -1;
    }
}

var pointList = new PointList();

var pointA = new Point(1, 1);
var pointB = new Point(1, 1);
var pointC = new Point(1, 2);

pointList.add(pointA);
alert(pointList.length); // 1

pointList.add(pointB);
alert(pointList.length); // 1

pointList.add(pointC);
alert(pointList.length); // 2

Solution 2

Typescript doesn't add any functionality to JavaScript. It's just "typed" and some syntax improvements.

So, there's not a way to override equals in an equivalent way to what you might have done in C#.

However, you would have ended up likely using a Hash or a strongly-typed Dictionary in C# to do an efficient lookup (in addition to the array potentially), rather than using an "index of" function.

For that, I'd just suggest you use an associative array structure to store the Points.

You'd do something like:

class Point {
    constructor(public x:Number = 0, 
        public y:Number = 0 ) {     
    }   
    public toIndexString(p:Point):String {
        return Point.pointToIndexString(p.x, p.y);  
    }       
    static pointToIndexString(x:Number, y:Number):String {
        return x.toString() + "@" + y.toString();   
    }       
}

var points:any = {};
var p: Point = new Point(5, 5);
points[p.toIndexString()] = p;

If a Point doesn't exist, checking the points associative array will returned undefined.

A function wrapping the array would be simple:

function findPoint(x:Number, y:Number):Point {
    return points[Point.pointToIndexString(x, y)];
}

Looping through all points would be easy to:

// define the callback (similar in concept to defining delegate in C#)
interface PointCallback {
    (p:Point):void;
}

function allPoints(callback:PointCallback):void {
    for(var k in points) {
        callback(points[k]);
    }
}

allPoints((p) => {
   // do something with a Point...
});

Solution 3

One thing you can do is try out linq.js. With that, you can do something like this:

var foundPoint = Enumerable.From(points)
    .SingleOrDefault(function(p) {
        return p.x == targetX && p.y == targety;
    });

... you could then just implement this function on your object

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
    static equals(points: Point[], candidate: Point): boolean {
        var foundPoint = Enumerable.From(points)
            .SingleOrDefault((p: Point): boolean => {
                return p.x == candidate.x && p.y == candidate.y;
             });
        return foundPoint != null;
    }
}

... and use it like this

var points = createPointsArray();
var point = getSomePoint();
// is point already in array?
var isPointInPoints = Point.equals(points, point)
Share:
20,301
lhk
Author by

lhk

Updated on November 07, 2021

Comments

  • lhk
    lhk over 2 years

    I need to store a list of points and check if a new point is already included in that list

    class Point {
        x: number;
        y: number;
        constructor(x: number, y: number) {
            this.x = x;
            this.y = y;
        }
    }
    
    window.onload = () => {
        var points : Point[] = [];
        points.push(new Point(1,1));
        var point = new Point(1,1);
        alert(points.indexOf(point)); // -1 
    }
    

    Obviously typescript uses comparison by reference but in this case that doesn't make sense. In Java or C# I would overload the equals method, in typescript that doesn't seem to be possible.

    I considered to loop through the array with foreach and check each entry for equality, but that seems rather complicated and would bloat the code.

    Is there something like equals in typescript ? How can I implement my own comparisons ?