objectro

Why?

Get/Set

You can get and set properties on an object using dot notation:

const { get, set } = require("objectro");

const input = {
  an: {
    example: {
      of: [
        {
          nested: {
            objects: [1, 2, 3]
          }
        }
      ]
    }
  }
};

get(input, "an.example.of[0].nested.objects[2]");
// -> 3

set(input, "an.example.of[0].nested.objects[2]", "nice!");

get(input, "an.example.of[0].nested.objects[2]");
// -> "nice!"

Transform

There are a couple ways to change the structure and content of the object: destructure/restructure and format.

By design a source object given to the objectro.transform() method wonโ€™t be changed, but a new object will be created referencing their content.

Destructure/Restructure

ES6 already allows us to do object destructuring, but sometimes we need something more programmatic.

Objectro allows you to transform objects similar to GraphQL, taking only the properties that you specify, additionally changing the property names on the outputted object:

  {
    fromPropertyName: "toPropertyName"
  }
const objectro = require("objectro").default;

const sourceObject = {
  name: "Matt Scheurich",
  dateOfBirth: "1983-04-17",
  nationality: "nz",
  location: "fr",
  languages: ["en", "fr"]
};

// The simplest transform takes the source object's property name and
// maps it to a new one
const transformedObject = objectro.transform(sourceObject, {
  name: "fullName",
  nationality: "placeOfBirth",
  location: "countryOfResidence"
});

console.log(transformedObject);

/*
  {
    fullName: "Matt Scheurich",
    placeOfBirth: "nz",
    countryOfResidence: "fr"
  }
  */

You could also just feed it a number of property names as well as transform maps:

const transformedObject = objectro.transform(
  sourceObject,
  "nationality",
  "location",
  {
    name: "fullName",
    nationality: "placeOfBirth",
    location: "countryOfResidence"
  }
);

console.log(transformedObject);

/*
  {
    nationality: "nz",
    location: "fr",
    fullName: "Matt Scheurich",
    placeOfBirth: "nz",
    countryOfResidence: "fr"
  }
  */

Format

Alongside destructuring, you can also format values with a function. The function can either return the formatted value or return an object that will be merged into the destination object:

  {
    fromPropertyName: formatValue(
      fromPropertyValue: any,
      fromPropertyName: string,
      source: object,
      destination: object
    ) -> mergeIntoDestinationObject: object
  }
const transformedObject = objectro.transform(sourceObject, {
  // Format the source property's value from a string to a Date
  dateOfBirth: value => new Date(value),
  // Transform the source object's property into a different format
  name: (value, propName, srcObj) => {
    // Format the value however you want...
    let [firstName, ...lastNames] = value.split(" ");
    // ... then return an object which will be merged with the
    // destination object
    return {
      firstName,
      lastName: lastNames.join(" ")
    };
  },
  // Parameter `value` will be undefined since `canSpeakEnglish` is
  // not on the source object
  canSpeakEnglish: (value, propName, srcObj) => ({
    canSpeakEnglish: srcObj.languages.includes("en")
  })
});

console.log(transformedObject);

/*
  {
    dateOfBirth: Sun Apr 17 1983 00:00:00 GMT+0000,
    firstName: "Matt",
    lastName: "Scheurich",
    canSpeakEnglish: true
  }
  */

Validate

objectro.validate() allows you to check if an object matches a set of conditions.

You can check if the object has the existence of certain properties (note that this does not check the value, only if the property exists):

const objectro = require("objectro").default;

const sourceObject = {
  name: "Matt Scheurich",
  dateOfBirth: "1983-04-17",
  age: 35,
  nationality: "nz",
  location: "fr",
  languages: ["en", "fr"],
  website: "https://www.lvl99.com",
  skills: {
    programming: [
      "html",
      "css",
      "javascript",
      "node",
      "frontend",
      "backend",
      "threejs"
    ],
    design: ["graphic", "publication", "typography", "illustration"],
    music: ["drums", "guitar", "production", "ableton"]
  },
  traits: ["positive", "helpful", "happy", "encouraging", "team member"]
};

objectro.validate(sourceObject, {
  has: ["name", "dateOfBirth", "skills.programming"]
}); // returns true because sourceObject has all three
// specified properties

There are also three rule modifiers you can use to nest more rules within:

    objectro.validate(sourceObject, {
        // Returns true only if all nested rules match
        all: {
          // Since `all` is defined above, it will apply to all the
          // options in `has` here.
          // This means that all the rules within the `has` rule
          // need to match in order to achieve a positive result.
          has: {
            languages: "en",
            "skills.programming": [
              "html",
              "css",
              "javascript",
              "frontend"
            ]
          },
          // Returns true on the first positive match
          any: {
            has: ["name", "age", "location"]
          },
          // Returns the inverse of the result, e.g. true = false
          not: {
            has: {
              traits: ["negative", "downer"]
            }
          }
        }
      }
    }) // returns true

You can easily check an objectโ€™s property values for one or many possible values:

objectro.validate(sourceObject, {
  has: {
    languages: "en",
    nationality: "nz",
    "skills.music": ["production", "ableton"]
  }
}); // returns true because sourceObject.nationality is "nz"
// and languages has a value of "en"

You can also feed it an array of accepted values:

objectro.validate(sourceObject, {
  has: {
    languages: ["fr", "de", "it"],
    location: ["fr", "de", "it"]
  }
}); // returns true because sourceObject.languages has a value of "fr"
// and sourceObject.location is "fr"

You can also validate individual properties with nested rule objects using the match rule:

objectro.validate(sourceObject, {
  match: {
    // We can match multiple rules on a single property
    name: {
      all: {
        type: "string",
        startsWith: "Matt"
      }
    },
    // Alternatively we can use a function to validate the property
    languages: value => typeof value === "array" && !value.length > 1,
    dateOfBirth: value =>
      new Date(value).getFullYear() >= new Date().getFullYear() - 18
  }
});

There are lots of rules you can use to validate objects or their properties.

For more information, view the API.