1.0.0 • Published 7 years ago

object-bee v1.0.0

Weekly downloads
2
License
MIT
Repository
github
Last release
7 years ago

object-bee.js is a lightweight, flexible library for manipulating plain-object data in JavaScript.

Why?

Manipulating data may need a lot of conditional judgment in usual, and it would bring a great costs of maintenance to the our project, as below:

function fixData (data) {
    if (data && data.info && data.info.name) { // mess!
        data.info['modify-name'] = data.info.name;
        delete data.info.name;
    }

    if (data && data.info && data.info.person && data.info.person.age) { // mess!
        delete data.info.person.age;
    }
}

To help improving maintainability and readability, object-bee access data by the structure of data itself, and deal with the value inner data only when the value exists —— just like bee finding flower :)

Rewrite the code above by object-bee:

const bee = require('object-bee');

function fixData (data) {
    bee(data, {
        info: {
            name: bee.rename('modify-name'),
            person: {
                age: bee.remove()
            }
        }
    });
}

The code becomes more meaningful and readable.

Installation

  • NPM install

    npm install object-bee -S
  • CDN provided by unpkg:

    <script src="https://unpkg.com/object-bee/dist/object-bee.min.js"></script>

Usage

Using function to manipulate data can contain more complex logic.

As object-bee iterating over all of key / value pairs, Handler function accepts each value and key as arguments, like function (value, key) {}, and the return value would replace the original value of data.

const bee = require('object-bee');

data = {
    a: 1,
    b: 2,
    sum: -1
};

bee(data, {
    sum () {
        return this.a + this.b;
    }
});

data.detail.sum === 3; // true

Context provide those features as below:

  • this: a reference to current data
  • this.$root: a reference to root data

    let data = {
        name: 'object-bee',
        detail: {
            bar: 'woo'
        }
    };
    
    bee(data, {
        detail: {
            foo () {
                this.bar === 'woo'; // true
                this.$root.name === 'object-bee'; // true
                this.$root.detail.bar === this.bar; // true
            }
        }
    })
  • this.$UNDEFINED: undefined returned by function will be ignore, If you want replace the original value with undefined, you should return this.$UNDEFINED instead, for examples:

    let data = {
        foo: 1,
        bar: 2
    };
    
    bee(data, {
    
        // no modify
        foo () {},
    
        // return 'this.$UNDEFINED' explicitly to assign undefined to data.bar
        bar () {
            return this.$UNDEFINED;
        }
    })
    // => { foo: 1, bar: undefined }
  • this.$config: specify config inner function:

    bee(data, {
        info () {
            this.$config({
                name: bee.rename('foo')
            });
        }
    });
  • this.$remove(): support to remove data, see action.remove below.

  • this.$rename(newName): support to rename, see action.rename below.
  • this.$ensure(): ensure current key to exist, see action.ensure below.
  • this.$mirror([path]): reuse config, see action.mirror below.

    bee(data, {
        foo () {
            this.$rename('bar');
            this.$remove();
            this.$ensure();
            this.$mirror();
        }
    });

Actions

object-bee provide several shorthand to simplify the usage of function. There are 3 types of actions, use in value place, key place, or normal place.

In value place

  • #remove: remove current key from data.

    bee(data, {
        unnecessaryKey: bee.remove()
    });
  • #ensure: ensure the key exist no matter whether it is undefined or being removed.

    bee({}, {
        newKey: bee.ensure()
    });
    // => { newKey: undefined }
    
    bee({}, {
        newKey: bee.ensure().remove() // #ensure has higher priority
    });
    // => { newKey: undefined }
  • #rename: rename the key

    bee(data, {
        bar: bee.rename('foo')
    });
  • #root: get value by path string related to root data.

  • #data: get value by path string related to current data:

    let data = {
        info: {
            detail: {
                name: 'object-bee',
            },
            foo: '',
            bar: ''
        }
    };
    
    bee(data, {
        foo: bee.root('info.detail.name'),
        bar: bee.data('detail.name')
    });

    Valid paths for #data and #root can be:

    path
    path.path.path
    list[2].path.path[0]
  • #mirror([path]): reuse config for recursive data, such as tree data, of which nodes have similar data structure. At this case, we can reuse config:

    let treeData = {
        name: 'root',
        child: {
            name: 'node',
            child: {
                name: 'leaf'
            }
        }
    };
    
    bee(treeData, {
        name () {
            return 'foo';
        }
        child: bee.mirror()
    });

    #mirror can accept a path to specify target config.

  • #noop: no-operation function used for placeholder.

In key place

  • #keep: same as #ensure, except it is used in computed key

    bee({}, {
        [bee.keep('newKey')] () {
            return 'bar';
        }
    });
    // => { newKey: 'bar' }
    
    bee({}, {
        [bee.keep('newKey')]: bee.remove() // #keep has higher priority
    });
    // => { newKey: undefined }
  • #match: match key by RegExp and String. It would apply default action to corresponding data of matching key.

    bee({
        num1: 1,
        num2: 2
    }, {
        [bee.match(/^num\d$/)] () {
            return 0;
        }
    });
    // => { num1: 0, num2: 0 }

    default action provided by #match has lowest priority than action of certain key.

  • #glob: like #match method, except it matches keys by wildcard characters

    bee({
        letterA: '',
        letterB: ''
    }, {
        [bee.glob('letter*')] () {
            return 'Z';
        }
    });
    // => { letterA: 'Z', letterB: 'Z' }

    default action provided by #glob has lowest priority than action of certain key.

Normal action

  • #create: by this method, object-bee would clone a new data, so original data would not be modified.

    let newData = bee(data, {});
    newData !== data; // true

Combination

All kinds of actions support to chain:

bee(data, {
    foo: bee.remove().ensure().rename('bar')
});

As all actions has its corresponding method inner function , it is recommend to use function to deal with more complex logic:

bee(data, {
    foo () {
        this.$ensure();
        this.$rename('bar');

        if (...) {
            this.$remove();
        }
    }
});

Applying actions and setting config at the same time:

let data = {
    detail: {}
};

bee(data, {
    detail () {
        this.$rename('info');

        this.$config({
            foo: bee.ensure()
        });
    }
});
// => { info: { foo: undefined } }

The code above may be suspected of messing up structure. If we want to keep the structure readable, we can use bee.CONFIG in computed key and assign action to it:

bee(data, {
    detail: {
        [bee.CONFIG]: bee.rename('info'),
        foo: bee.ensure();
    }
});

// or function
bee(data, {
    detail: {
        [bee.CONFIG] () {
            this.$rename('info')
        },
        foo: bee.ensure();
    }
});

This code do same thing as last code.