API Docs for: 0.0.1
Show:

File: src/js-ext/lib/array.js

/**
 *
 * Pollyfils for often used functionality for Arrays
 *
 * <i>Copyright (c) 2014 ITSA - https://github.com/itsa</i>
 * New BSD License - http://choosealicense.com/licenses/bsd-3-clause/
 *
 * @module js-ext
 * @submodule lib/array.js
 * @class Array
 *
 */

"use strict";

require('polyfill/polyfill-base.js');

var createHashMap = require('js-ext/extra/hashmap.js').createMap,
    TYPES = createHashMap({
       'undefined' : true,
       'number' : true,
       'boolean' : true,
       'string' : true,
       '[object Function]' : true,
       '[object RegExp]' : true,
       '[object Array]' : true,
       '[object Date]' : true,
       '[object Error]' : true,
       '[object Promise]' : true
    }),
    isObject, objSameValue, deepCloneObj, cloneObj, valuesAreTheSame;

isObject = function (item) {
    return !!(!TYPES[typeof item] && !TYPES[({}.toString).call(item)] && item);
};

objSameValue = function(obj1, obj2) {
    var keys = Object.getOwnPropertyNames(obj1),
        keysObj2 = Object.getOwnPropertyNames(obj2),
        l = keys.length,
        i = -1,
        same, key;
    same = (l===keysObj2.length);
    // loop through the members:
    while (same && (++i < l)) {
        key = keys[i];
        same = obj2.hasOwnProperty(key) ? valuesAreTheSame(obj1[key], obj2[key]) : false;
    }
    return same;
};

deepCloneObj = function (obj, descriptors) {
    var m = Object.create(Object.getPrototypeOf(obj)),
        keys = Object.getOwnPropertyNames(obj),
        l = keys.length,
        i = -1,
        key, value, propDescriptor;
    // loop through the members:
    while (++i < l) {
        key = keys[i];
        value = obj[key];
        if (descriptors) {
            propDescriptor = Object.getOwnPropertyDescriptor(obj, key);
            if (propDescriptor.writable) {
                Object.defineProperty(m, key, propDescriptor);
            }
            if ((Object.isObject(value) || Array.isArray(value)) && ((typeof propDescriptor.get)!=='function') && ((typeof propDescriptor.set)!=='function') ) {
                m[key] = cloneObj(value, descriptors);
            }
            else {
                m[key] = value;
            }
        }
        else {
            m[key] = (Object.isObject(value) || Array.isArray(value)) ? cloneObj(value, descriptors) : value;
        }
    }
    return m;
};

cloneObj = function(obj, descriptors, target) {
    var copy, i, len, value;

    // Handle Array
    if (obj instanceof Array) {
        copy = target || [];
        len = obj.length;
        for (i=0; i<len; i++) {
            value = obj[i];
            copy[i] = (Object.isObject(value) || Array.isArray(value)) ? cloneObj(value, descriptors) : value;
        }
        return copy;
    }

    // Handle Date
    if (obj instanceof Date) {
        copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Object
    if (Object.isObject(obj)) {
        return obj.deepClone(descriptors);
    }

    return obj;
};

valuesAreTheSame = function(value1, value2) {
    var same;
    // complex values need to be inspected differently:
    if (isObject(value1)) {
        same = isObject(value2) ? objSameValue(value1, value2) : false;
    }
    else if (Array.isArray(value1)) {
        same = Array.isArray(value2) ? value1.sameValue(value2) : false;
    }
    else if (value1 instanceof Date) {
        same = (value2 instanceof Date) ? (value1.getTime()===value2.getTime()) : false;
    }
    else {
        same = (value1===value2);
    }
    return same;
};


(function(ArrayPrototype) {

    /**
     * Checks whether an item is inside the Array.
     * Alias for (array.indexOf(item) > -1)
     *
     * @method contains
     * @param item {Any} the item to seek
     * @return {Boolean} whether the item is part of the Array
     */
    ArrayPrototype.contains = function(item) {
        return (this.indexOf(item) > -1);
    };

    /**
     * Removes an item from the array
     *
     * @method remove
     * @param item {any|Array} the item (or an hash of items) to be removed
     * @param [arrayItem=false] {Boolean} whether `item` is an arrayItem that should be treated as a single item to be removed
     *        You need to set `arrayItem=true` in those cases. Otherwise, all single items from `item` are removed separately.
     * @chainable
     */
    ArrayPrototype.remove = function(item, arrayItem) {
        var instance = this,
            removeItem = function(oneItem) {
                var index = instance.indexOf(oneItem);
                (index > -1) && instance.splice(index, 1);
            };
        if (!arrayItem && Array.isArray(item)) {
            item.forEach(removeItem);
        }
        else {
            removeItem(item);
        }
        return instance;
    };

    /**
     * Replaces an item in the array. If the previous item is not part of the array, the new item is appended.
     *
     * @method replace
     * @param prevItem {any} the item to be replaced
     * @param newItem {any} the item to be added
     * @chainable
     */
    ArrayPrototype.replace = function(prevItem, newItem) {
        var instance = this,
            index = instance.indexOf(prevItem);
        (index!==-1) ? instance.splice(index, 1, newItem) : instance.push(newItem);
        return instance;
    };

    /**
     * Inserts an item in the array at the specified position. If index is larger than array.length, the new item(s) will be appended.
     * If the item already exists, it will be moved to its new position, unless `duplicate` is set true
     *
     * @method insertAt
     * @param item {any|Array} the item to be replaced, may be an Array of items
     * @param index {Number} the position where to add the item(s). When larger than Array.length, the item(s) will be appended.
     * @param [duplicate=false] {boolean} if an item should be duplicated when already in the array
     * @chainable
     */
    ArrayPrototype.insertAt = function(item, index, duplicate) {
        var instance = this,
            prevIndex;
        if (!duplicate) {
            prevIndex = instance.indexOf(item);
            if (prevIndex===index) {
                return instance;
            }
            (prevIndex > -1) && instance.splice(prevIndex, 1);
        }
        instance.splice(index, 0, item);
        return instance;
    };

    /**
     * Shuffles the items in the Array randomly
     *
     * @method shuffle
     * @chainable
     */
    ArrayPrototype.shuffle = function() {
        var instance = this,
            counter = instance.length,
            temp, index;
        // While there are elements in the instance
        while (counter>0) {
            // Pick a random index
            index = Math.floor(Math.random() * counter);

            // Decrease counter by 1
            counter--;

            // And swap the last element with it
            temp = instance[counter];
            instance[counter] = instance[index];
            instance[index] = temp;
        }
        return instance;
    };

    /**
     * Returns a deep copy of the Array.
     * Only handles members of primary types, Dates, Arrays and Objects.
     *
     * @method deepClone
     * @param [descriptors=false] {Boolean} whether to use the descriptors when cloning
     * @return {Array} deep-copy of the original
     */
     ArrayPrototype.deepClone = function (descriptors) {
        return cloneObj(this, descriptors);
     };

    /**
     * Compares this object with the reference-object whether they have the same value.
     * Not by reference, but their content as simple types.
     *
     * Compares both JSON.stringify objects
     *
     * @method sameValue
     * @param refObj {Object} the object to compare with
     * @return {Boolean} whether both objects have the same value
     */
    ArrayPrototype.sameValue = function(refArray) {
        var instance = this,
            len = instance.length,
            i = -1,
            same;
        same = (len===refArray.length);
        // loop through the members:
        while (same && (++i < len)) {
            same = valuesAreTheSame(instance[i], refArray[i]);
        }
        return same;
    };

    /**
     * Sets the items of `array` to the instance. This will refill the array, while remaining the instance.
     * This way, external references to the array-instance remain valid.
     *
     * @method defineData
     * @param array {Array} the Array that holds the new items.
     * @param [clone=false] {Boolean} whether the items should be cloned
     * @chainable
     */
    ArrayPrototype.defineData = function(array, clone) {
        var thisArray = this,
            len, i;
        thisArray.empty();
        if (clone) {
            cloneObj(array, true, thisArray);
        }
        else {
            len = array.length;
            for (i=0; i<len; i++) {
                thisArray[i] = array[i];
            }
        }
        return thisArray;
    },

    /**
     * Merges `array` into this array (appended by default).
     *
     * @method concatMerge
     * @param array {Array} the Array to be merged
     * @param [prepend=false] {Boolean} whether the items prepended
     * @param [clone=false] {Boolean} whether the items should be cloned
     * @param [descriptors=false] {Boolean} whether to use the descriptors when cloning
     * @chainable
     */
    ArrayPrototype.concatMerge = function(array, prepend, clone, descriptors) {
        var instance = this,
            mergeArray = clone ? array.deepClone(descriptors) : array;
        if (prepend) {
            mergeArray.reduceRight(function(coll, item) {
                coll.unshift(item);
                return coll;
            }, instance);
        }
        else {
            mergeArray.reduce(function(coll, item) {
                coll[coll.length] = item;
                return coll;
            }, instance);
        }
        return instance;
    };

    /**
     * Empties the Array by setting its length to zero.
     *
     * @method empty
     * @chainable
     */
    ArrayPrototype.empty = function() {
        this.length = 0;
        return this;
    };

}(Array.prototype));