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

coalesce-strategy

v1.0.4

Published

A nodejs module to coalesce javascript objects into a central model

Downloads

5

Readme

NPM version Build status Test coverage Dependency Status License Downloads

Coalesce Strategy

MEGANPM

coalesce-strategy is a nodejs module that allows you to create relatively* complex merging strategies for javascript objects. This module really shines if you're consuming json objects from various services and you need fine grained control over which fields you'll use in your application.

*coalesce-strategy doesn't support deep priorities i.e flat objects are preferred.

Install

$ npm install --save coalesce-strategy

Purpose

For example, if you created an app that gathered ID3 tag data for songs from various services, you would probably want to cherry pick which field gets used from each service. e.g always use song titles from Spotify, but only use genres from Lastfm.

And before now you would probably hard code this manually. And this may be ok for 2 services, but when you need to coalesce objects from 10+ services the code gets unwieldy and unmanageable. Especially in a real life scenario where the items you're merging may be missing fields. e.g if Lastfm was missing genre data, you would probably want to fallback to Spotify.

And that's where coalesce-strategy comes into play. It allows you to define a strategy object which has the strategies and priorities that should be used to coalesce your items.

Quick Example

Based on our song meta data example above, here's how we would solve our problem using coalesce-strategy

First lets define our strategy file(basic_strat.json):

{
  "strategies": {
    "Spotify" : { "priorities": { "title": 10 } },
    "Lastfm" : { "priorities": { "genre": 3,  "releaseDate": 3 } } }
}    

Now for the coalescing:

// load our strategy
var strategy = require('./basic_strat.json');

// define our central model to coalesce against
var songModel = {
    title: '',
    genre: '',
    releaseDate: '',
    length: ''
};

var merger = require('coalesce-strategy')(strategy, songModel);

var items = [];
items.push(merger.createItem('Spotify', {
    title: 'The FooBars',
    genre: 'country-horror', // ;)
    releaseDate: '10-21-2200' // LastFM has a higher priority,
                              // but it doesn't exist on LastFM's item
}));
items.push(merger.createItem('Lastfm', {
    title: 'The Fo&^$o_Bars!',
    genre: 'dubstep',
    pizzaFactor: 'supreme' // doesn't exist on the model
}));

// anonymous items have priorities of 0
items.push(merger.createItem({
    title: 'Fizzies',    // priority == 0; loses to Spotify
    length: '10 Minutes' // priority == 0;
}));

// coalesce our items
merger.merge(items, function(err, result) {
    console.log(result);     // => { title: 'The FooBars', genre: 'dubstep', releaseDate: '10-21-2200', length: '10 Minutes' }
});

By using coalesce-strategy we no longer have to hard code the properties we wish to use and allows us to quickly and easily modify existing priorities, as well as add new strategies when we add additional services. All without changing existing code logic.

Documentation

Methods

Modifiers

Methods

Wraps item into an object with the specified a id which can be understood by the library.

The id should match an existing id in the strategy object. Items without an id will be assigned a randomly generated id and all properties will be treated as having an priority of 0.

Arguments

  • id - The id of the strategy.
  • item - The data/payload.

Returns

  • {object} - a strategy item.

Examples

// strategy.json
{
  "strategies": {
    "Spotify" : { "priorities": { "title": 10 } } }
}
// Anonymous item
var item1 = merger.createItem({
    title: 'Fizzies',
    length: '10 Minutes'
});

// Item with an id
var item2 = merger.createItem('Spotify', { // <-- matches id in strategy.json
    title: 'The FooBars',
    genre: 'country'
});

Merges/Coalesces an array of strategy items into a central model.

Arguments

  • items - An array of strategy items.
  • callback(err, result) - A callback which is called when the coalescing is complete. result will be the initial model with its property values modified to conform to the desired priorities.

As of writing the err parameter is unused and will always be assigned null. This is to remain compatible with libraries that require the traditional callback(err, data) pattern.

Examples

// basic_strat.json
{
  "strategies": {
    "Spotify" : { "priorities": { "title": 10 } },
    "Lastfm" : { "priorities": { "genre": 3,  "releaseDate": 3 } } }
}    
// load our strategy
var strategy = require('./basic_strat.json');

// define our central model to coalesce against
var songModel = {
    title: '',
    genre: '',
    releaseDate: '',
    length: ''
};

// initialize coalesce strategy library
var merger = require('coalesce-strategy')(strategy, songModel);

var items = [];
items.push(merger.createItem('Spotify', {
    title: 'The FooBars',
    genre: 'country-horror', // ;)
    releaseDate: '10-21-2200' // LastFM has a higher priority,
                              // but it doesn't exist on LastFM's item
}));
items.push(merger.createItem('Lastfm', {
    title: 'The Fo&^$o_Bars!',
    genre: 'dubstep',
    pizzaFactor: 'supreme' // doesn't exist on the model
}));

// anonymous items have priorities of 0
items.push(merger.createItem({
    title: 'Fizzies',    // priority == 0; loses to Spotify
    length: '10 Minutes' // priority == 0;
}));

// coalesce our items
merger.merge(items, function(err, result) {
    console.log(result);     // => { title: 'The FooBars', genre: 'dubstep', releaseDate: '10-21-2200', length: '10 Minutes' }
});

Modifiers

Modifiers are a powerful feature of coalesce-strategy that allows intricate customization of the coalescing process. Coalesce-strategy supports blacklisting and whitelisting of properties, as well as a few other customization options.

To understand the modifier documentation below, it's important to understand the various levels/ways a modifier can be set inside a strategy file(model, strategy, priority).

Modifiers set on the strategy level affects all properties on that item(including ones that aren't explicitly defined).

A strategy object inside a strategy file does not need to include every property an item may have. Anything not given an explicit priority will be assumed to have a priority of 0.

// SomeStrategyFile.json
{
  "model" : {
    "ignore": ["title"] // set on the "Model" level
  },

  "strategies": {
    "Spotify": { // Spotify Strategy
      "winOnDefault": false, // set on the "Strategy" level

      "priorities": {
        "title": 10,

        "release-date": { // setting a priority using an object
          "winOnDefault": true, // set on the "Priority" level
          "priority": 5
        },

        "genre": 2
      }
    }
  }
}   

Generally the property with the highest priority always wins, but when two or more properties have the same priority, we refer to this as the "Default" or "Conflict" state. This modifier allows coalesce-strategy to decide who should "win" in that scenario.

Without the use of winOnDefault the item who appears later in the array of strategy items wins.

Supported On

strategy priority

Type

boolean

Examples

//basic_strat.json
{
  "strategies": {
    "Spotify" : {
      "priorities": { "title": 10 }
    },
    "Lastfm" : {
      "priorities": {
        "title": {
          "winOnDefault": true,
          "priority": 10
        },
        "releaseDate": 3
      },
      "rdio" : {
        "winOnDefault": false,
        "priorities": {
          "releaseDate": 3
        }
      }
    }
  }
}
var strategy = require('./basic_strat.json');
var songModel = {
    title: '',
    genre: '',
    releaseDate: '',
    length: ''
};

var merger = require('coalesce-strategy')(strategy, songModel);

var items = [];
items.push(merger.createItem('Spotify', {
    title: 'The FooBars', // conflicts with Lastfm but LastFM has winOnDefault true
    releaseDate: 'Spotify releaseDate'
}));
items.push(merger.createItem('Lastfm', {
    title: 'The Fo&^$o_Bars!', // will beat spotify in conflict(winOnDefault)
    releaseDate: 'Lastfm releaseDate' // conflicts with Lastfm but rdio has winOnDefault false
}));
items.push(merger.createItem('rdio', {
    title: 'Fizzies',
    releaseDate: 'rdio releaseDate' // has winOnDefault false
}));

merger.merge(items, function(err, result) {
    console.log(result); // => { title: 'The Fo&^$o_Bars!', genre: '', releaseDate: 'Lastfm releaseDate', length: '' }
});

A blacklist of properties. Properties that are blacklisted will be ignored during the coalescing process.

Supported On

model strategy

Type

array

Examples

//basic_strat.json
{
  "model": {
    "ignore": ["author"]
  },
  "strategies": {
    "BarnesNobles": {},
    "Amazon": {
      "ignore": ["summary"]
    }
  }
}
var strategy = require('./basic_strat.json');
var bookModel = {
    title: '',
    author: '',
    summary: '',
    rating: ''
};
var merger = require('coalesce-strategy')(strategy, bookModel);
var items = [];
items.push(merger.createItem('BarnesNobles', {
    title: 'Stack Underflow', // priority == 0
    author: 'Manny', // ignored on model
    summary: 'A story about a little byte that could.'
}));
items.push(merger.createItem('Amazon', {
    title: 'The Fo&^$o_Bars!', // priority == 0; but is later in the array
    author: 'Pacquiao', // ignored on model
    summary: 'Fake summary' // ignored on strategy
}));
merger.merge(items, function(err, result) {
    console.log(result); // => { title: 'The Fo&^$o_Bars!', author: '', summary: 'A story about a little byte that could.', rating: '' }
});

A whitelist of properties. Only properties listed in the array will be used to populate the central model.

Supported On

model strategy

Type

array

Examples

//basic_strat.json
{
  "model": {
    "useOnly": ["studio", "rating", "revenue"]
  },
  "strategies": {
    "IMDB": {
      "useOnly": ["rating"]
    },
    "metacritic": {
      "useOnly": ["title", "revenue"]
    }
  }
}
var strategy = require('./basic_strat.json');
var movieModel = {
    title: '',
    studio: '',
    rating: '',
    revenue: ''
};
var merger = require('coalesce-strategy')(strategy, movieModel);
var items = [];
items.push(merger.createItem('IMDB', {
    title: 'Pizza Party',
    studio: 'Lions Gate',
    revenue: '10M',
    rating: '5.9' // only this property is whitelisted/allowed
}));
items.push(merger.createItem('metacritic', {
    title: 'The Matrix', // whitelisted by strategy; but isn't whitelisted by the model
    studio: 'WB',
    revenue: '900M', // whitelisted
    rating: '1.0'
}));
items.push(merger.createItem({ // anonymous item
    studio: 'RocketJump' // whitelisted
}));
merger.merge(items, function(err, result) {
    console.log(result); // => { title: '', studio: 'RocketJump', rating: '5.9', revenue: '900M' }
});

Shifts the priorities of all the properties on an strategy. This also applies to properties that are not explicitly defined in the strategy file. The property's final priority will be the sum of the baseline and the property's own priority(if defined).

Supported On

strategy

Type

number

Examples

//basic_strat.json
{
  "strategies": {
    "npm": {
      "baseline": 10,
      "priorities": {
        "maintainer": 5
      }
    },
    "enterprise":{
      "priorities": {
        "maintainer": 8,
        "packageName": 6
      }
    }
  }
}
var strategy = require('./basic_strat.json');
var npmModel = {
    packageName: '',
    maintainer: ''
};
var merger = require('coalesce-strategy')(strategy, npmModel);
var items = [];
items.push(merger.createItem('npm', {
    // notice how packageName wasn't given an explicit priority
    packageName: 'coalesce-strategy', // Baseline shift; priority == 10
    maintainer: 'vorror' // priority == 15(10+5)
}));
items.push(merger.createItem('enterprise', {
    packageName: 'fake package', // priority == 6
    maintainer: 'other_guy' // priority == 8
}));
merger.merge(items, function(err, result) {
    console.log(result); // => { packageName: 'coalesce-strategy', maintainer: 'vorror' }
});

Sets the priority of a particular property. Useful when combining multiple modifiers at the priority level.

Supported On

priority

Type

number

Examples

//basic_strat.json
{
  "strategies": {
    "npm": {
      "priorities": {
        "maintainer": 5,
        "downloads": {
        	"priority": 10,
        	"winOnDefault": true
        }
      }
    }
  }
}

Any property/key on the central model object that has been assigned a function will be ignored during the coalescing process.

Supported On

model

Type

boolean

Examples

//basic_strat.json
{
  "model": {
    "skipKeysWithFunctionValues": true
  },
  "strategies": {
    "npm": {
      "priorities": { "maintainer": 5 } } }
}
var strategy = require('./basic_strat.json');
var npmModel = {
    packageName: '',
    calculateDownloads: function() {
        console.log('Some function');
    }
};
var merger = require('coalesce-strategy')(strategy, npmModel);
var items = [];
items.push(merger.createItem('npm', {
    packageName: 'coalesce-strategy',
    calculateDownloads: 'Hello World' // ignored because typeof calculateDownloads === function on npmModel
}));
merger.merge(items, function(err, result) {
    console.log(result); // => { packageName: 'coalesce-strategy', calculateDownloads: [Function] }
});

Optionally disable coalescing properties with empty values. Default is true.

Supported On

model

Type

boolean

Examples

//basic_strat.json
{
  "model": { "allowMergingOfEmptyValues": false }
}
var strategy = require('./basic_strat.json');
var npmModel = {
    packageName: 'coalesce-strategy'
};
var merger = require('coalesce-strategy')(strategy, npmModel);
var items = [];
items.push(merger.createItem('npm', {
    packageName: '' // empty so it will be ignored
}));
merger.merge(items, function(err, result) {
    console.log(result); // => { packageName: 'coalesce-strategy' }
});

And just so there's no confusion as to what qualifies as empty:

var merger = require('coalesce-strategy');
console.log(merger.tests.isEmpty('')); // => true
console.log(merger.tests.isEmpty(String())); // => true
console.log(merger.tests.isEmpty([])); // => true
console.log(merger.tests.isEmpty({})); // => true
console.log(merger.tests.isEmpty(null)); // => true
console.log(merger.tests.isEmpty(undefined)); // => true
console.log(merger.tests.isEmpty({a: ''})); // => false
console.log(merger.tests.isEmpty('Stuff')); // => false
console.log(merger.tests.isEmpty(function(){})); // => false
console.log(merger.tests.isEmpty(false)); // => false
console.log(merger.tests.isEmpty(true)); // => false
console.log(merger.tests.isEmpty(10)); // => false

Never EVER use anything under tests. It's only exposed for unit testing purposes. You have been warned.


License

The MIT License (MIT)

Copyright (c) 2015 Paul J. Miller

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.