npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@rbxts/deep-equal

v0.8.1

Published

Recursive comparator for ROBLOX projects.

Downloads

112

Readme

Recursive comparator for ROBLOX projects.


Demo

import { deepEqual } from "@rbxts/deep-equal";

const result = deepEqual([1,2,3], [1,2,4]);
warn(result);

ROBLOX console output

Installation

Install deep-equal with your preferred package manager.

npm

npm install @rbxts/deep-equal

pnpm

pnpm add @rbxts/deep-equal

yarn

yarn add @rbxts/deep-equal

Overview

deep-equal is an implementation of the common recrusive comparator, but for roblox projects.

Since roblox tables are typically compared by reference, utilizing deep-equal allows you to compare them by nested values, and get specific failure data on where the comparison failed.

deep-equal is also able to differentiate between arrays and non-array tables, so your comparisons on two arrays can be more array specific.

[!NOTE] deep-equal is built to primarily be used as an implementor of deep equal algorithms for other assertion libraries, or more complex validation systems.

That's why failures return an object of specific failure data, instead of throwing an error.

Usage

Basic Usage

Common Types

Strings
deepEqual("daymon", "michael");
/**
 * {
 *   failType: FailureType.DIFFERENT_VALUES,
 *   leftValue: "daymon",
 *   rightValue: "michael"
 * }
 */
Numbers
deepEqual(5, 10);
/**
 * {
 *   failType: FailureType.DIFFERENT_VALUES,
 *   leftValue: 5,
 *   rightValue: 10
 * }
 */
Booleans
deepEqual(true, false);
/**
 * {
 *   failType: FailureType.DIFFERENT_VALUES,
 *   leftValue: true,
 *   rightValue: false
 * }
 */
Different Types
deepEqual(5, "5");
/**
 * {
 *   failType: FailureType.DIFFERENT_TYPES,
 *   leftValue: 5,
 *   leftType: "number",
 *   rightValue: "5",
 *   rightType: "string"
 * }
 */

Arrays

deepEqual([1,2,3], [1,2,4]);
/**
 * {
 *   failType: FailureType.MISSING_ARRAY_VALUE,
 *   leftValue: [1,2,3],
 *   rightValue: [1,2,4],
 *   leftMissing: [4],
 *   rightMissing: [3]
 * }
 */

Tables

Different Property Values
deepEqual({
  name: "daymon",
  age: 100
}, {
  name: "daymon",
  age: 200
});
/**
 * {
 *   failType: FailureType.DIFFERENT_VALUES,
 *   leftValue: 100,
 *   rightValue: 200,
 *   path: "age"
 * }
 */
Missing Array Values
deepEqual({
  name: "daymon",
  cars: ["Tesla", "Civic"]
}, {
  name: "daymon",
  cars: ["Tesla"]
});
/**
 * {
 *   failType: FailureType.MISSING_ARRAY_VALUE,
 *   leftValue: ["Tesla", "Civic"],
 *   rightValue: ["Tesla"],
 *   rightMissing: ["Civic"],
 *   path: "cars"
 * }
 */
Missing Properties
deepEqual({
  name: "daymon",
  age: 100
}, {
  name: "daymon",
});
/**
 * {
 *   failType: FailureType.MISSING,
 *   leftValue: 100,
 *   rightValue: undefined,
 *   path: "age"
 * }
 */
Nested Tables
deepEqual({
  name: "daymon",
  details: {
    origin: {
      city: "Kansas City",
      state: "MO"
    }
  }
}, {
  name: "daymon",
  details: {
    origin: {
      city: "Kansas City",
      state: "KS"
    }
  }
});
/**
 * {
 *   failType: FailureType.DIFFERENT_VALUES,
 *   leftValue: "MO",
 *   rightValue: "KS",
 *   path: "details.origin.state"
 * }
 */

Roblox Types

Roblox types can be split into two categories: ones that are compared by value and ones that are compared by reference.

Value Types
deepEqual(new Vector3(1,2,3), new Vector3(1,2,3)); // pass

deepEqual(new Vector3(1,2,3), new Vector3(2,4,6));
/**
 * {
 *   failType: FailureType.DIFFERENT_VALUES,
 *   leftValue: (1, 2, 3),
 *   rightValue: (2, 4, 6)
 * }
 */
Reference Types

[!TIP] You can provide custom comparators for comparing reference types.

deepEqual(new OverlapParams(), new OverlapParams());
/**
 * {
 *   failType: FailureType.DIFFERENT_REFERENCE,
 *   leftValue: "OverlapParams{...}",
 *   rightValue: "OverlapParams{...}"
 * }
 */

Configuration

You can optionally provide some configuration in your calls to deep-equal, or set them at the global level.

Ignoring Types

An array of types to ignore.

deepEqual({
  name: "daymon",
  position: new Vector3(1,2,3),
  cars: ["Tesla", "Civic"]
}, {
  name: "daymon",
  position: new Vector3(2,4,6),
  cars: ["Tesla"]
}, { ignore: ["Vector3"] });
/**
 * {
 *   failType: FailureType.MISSING_ARRAY_VALUE,
 *   leftValue: ["Tesla", "Civic"],
 *   rightValue: ["Tesla"],
 *   rightMissing: ["Civic"],
 *   path: "cars"
 * }
 */

When the left or right is any of these types, it will be skipped; effectively "ignoring" it for the purposes of checking if two objects are equal.

Reference Only

An array of types to only compare by reference.

deepEqual(new Vector3(1,2,3), new Vector3(2,4,6), { referenceOnly: ["Vector3"] });
/**
 * {
 *   failType: FailureType.DIFFERENT_REFERENCE,
 *   leftValue: (1, 2, 3),
 *   rightValue: (2, 4, 6)
 * }
 */

Some roblox types are compared by value instead of reference. Adding said types to this array will instead force them to be compared by reference instead, with a FailureType.DIFFERENT_REFERENCE failure type attached.

Check Right Missing

Check for missing values from the right in comparison to the left.

deepEqual({
  name: "daymon",
  cars: ["Tesla", "Civic"]
}, {
  name: "daymon",
  cars: ["Tesla", "Mustang"]
}, { rightMissing: true });
/**
 * {
 *   failType: FailureType.MISSING_ARRAY_VALUE,
 *   leftValue: ["Tesla", "Civic"],
 *   rightValue: ["Tesla", "Mustang"],
 *   leftMissing: ["Mustang"]
 *   rightMissing: ["Civic"],
 *   path: "cars"
 * }
 */

Since this setting is enabled by default, its usage is primarily for disabling it when you only want to check certain values- while ignoring others.

deepEqual({
  name: "daymon",
  details: {
    origin: {
      city: "Kansas City",
      state: "MO"
    }
  }
}, {
  details: {
    origin: {
      state: "KS"
    }
  }
}, { rightMissing: false });
/**
 * {
 *   failType: FailureType.DIFFERENT_VALUES,
 *   leftValue: "MO",
 *   rightValue: "KS",
 *   path: "details.origin.state"
 * }
 */

Compare Arrays In-Order

Compares arrays in order instead of by missing.

deepEqual({
  name: "daymon",
  cars: ["Tesla", "Civic"]
}, {
  name: "daymon",
  cars: ["Tesla"]
});
/**
 * {
 *   failType: FailureType.DIFFERENT_VALUES,
 *   leftValue: "Civic",
 *   rightValue: undefined,
 *   path: "cars.[1]"
 * }
 */

By default, arrays are looked at in their entirety and a list of elements missing from either (depending on if checkRightMissing is enabled) is provided.

You can enable inOrder to instead throw at the first failure, with data pertaining to where the failure occurred.

Useful when working with arrays of complex types, or when you want to assert that an array is "exactly in order".

Custom Comparators

If you want to provide your own behavior for certain types (like reference types) or override existing behavior, you can provide your own methods for certain types.

const checkInstance: CustomChecker<"Instance"> = (config, left, right) => {
  if(left.Name === right.Name) return Option.none();
  return return Option.some({
    failType: FailureType.DIFFERENT_VALUES,
    leftValue: left.Name,
    rightValue: right.Name,
    leftMissing: [],
    rightMissing: [],
    leftType: "Instance",
    rightType: "Instance",
    path: "",
  });
};

deepEqual(Workspace.Part1, Workspace.Part2, {
  customCheckers: {
    Instance: checkInstance
  }
});

![TIP] By default, deep-equal provides some checkers for certain reference types out of the box that are already attached to the default config!

Take a look at the checkers directory to see which types, and further examples of custom comparators.

Setting Global Configuration

If you have configurations you want to apply to all usages of deep-equal, you can set a global configuration.

setDefaultDeepEqualConfig({
  customCheckers: {
    Instance: checkInstance
  }
});

deepEqual(Workspace.Part1, Workspace.Part2); // inherits the default config

[!WARNING] setDefaultDeepEqualConfig replaces any previously set default config- it does not merge them.

You can use the provided getDefaultDeepEqualConfig and mergeConfigs functions to add this behavior on your own, but the default config is not intended to be used in such a way- which is why this behavior is not provided out of the box.

Roadmap

  • Provide better circular reference identification in the failure data.
  • Provide a support library for deep Instance comparators.
  • Implement workflow for test coverage.
  • Implement tests for provided comparators.
  • Add workflow for checking API diff and version bumping according to semver.
  • Add note in contributing about checking the api diff.

Contributing

If you're interested in contributing to deep-equal, give the CONTRIBUTING doc a read.

License

Apache 2.0