2017年1月19日 星期四

(js)Map

// 2017/01/20

if (typeof window == 'undefined') {
    // node.js
    module.exports = $Map_;
}


/**
 * 類似(Map)的功能
 */
function $Map_(dataList) {
    var self = this;
    this.fn = this.constructor;

    this.size = 0;

    // this.keys = []; // [key, key.......]
    this.dataList = []; // [{k: key, v: value}, {k: key, v: value}.....]


    function __constructor() {
        if (Array.isArray(dataList)) {
            self.dataList = dataList;
            self.size = self.dataList.length;
        }
    };

    __constructor();
};

////////////////////////////////////////////////////////////////////////////////
(function() {
    /**
     *
     * @return {Map}
     */
    this.set = function(key, value) {
        var index;

        var data = {
            'k': key,
            'v': value
        };

        if ((index = this.indexOfKey(key)) >= 0) {
            // 若(key)已經存在
            this.dataList[index] = data;
        } else {
            this.dataList.push(data);
            this.size = this.dataList.length;
        }

        return this;
    };
    /* ====================================================================== */
    this.get = function(key) {
        var index, index, _k, valueObj, checkCount = 0;

        if ((index = this.indexOfKey(key)) <= 0) {
            // 若沒找到(key)
            return undefined;
        }
        /* ---------------------------------- */
        var data = this.dataList[index];

        return data.v;
    };
}).call($Map_.prototype);

////////////////////////////////////////////////////////////////////////////////
(function() {

    /* ====================================================================== */
    this.clear = function() {
        this.dataList = [];
        this.size = this.dataList.length;
    };
    /* ====================================================================== */
    this.delete = function(key) {
        var index, result = false;

        if ((index = this.indexOfKey) >= 0) {

            this.dataList.splice(index, 1);
            this.size = this.dataList.length;

            result = true;
        }
        return result;
    };
}).call($Map_.prototype);
////////////////////////////////////////////////////////////////////////////////

(function() {
    this.indexOfKey = function(key) {
        var index = -1;

        if (key == null) {
            return index;
        }

        this.dataList.some(function(data, i) {
            var _key = data.k || undefined;
            if (_key === key) {
                index = i;
                return true;
            }
        });
        return index;
    };

    /* ====================================================================== */
    this.has = function(key) {
        var index, result = false;

        if ((index = this.indexOfKey(key)) >= 0) {
            result = true;
        }
        return result;
    };
}).call($Map_.prototype);

////////////////////////////////////////////////////////////////////////////////

(function() {

    /**
     * 主要的不同處,返回(array)
     *
     * @return {array} [keys]
     */
    this.keys = function() {
        var keys_array = [];

        this.dataList.forEach(function(data) {
            var key = data.k;
            keys_array.push(key);
        });

        return keys_array;
    };
    /* ====================================================================== */
    /**
     * 主要的不同處,返回(array)
     *
     * @return {array} [values]
     */
    this.values = function() {
        var values_array = [];

        this.dataList.forEach(function(data) {
            var values = data.v;
            values_array.push(values);
        });

        return values_array;
    };
}).call($Map_.prototype);
////////////////////////////////////////////////////////////////////////////////
(function() {
    this.entries = function() {
        var result = [];
        this.dataList.forEach(function(obj) {
            result.push([obj.k, obj.v]);
        });

        return this.fn.makeIterator(result);
    };
}).call($Map_.prototype);



////////////////////////////////////////////////////////////////////////////////
(function() {
    this.forEach = function(callback, _this) {

        var self = this;
        _this = _this || this;

        this.dataList.forEach(function(data, i) {
            var k = data.k;
            var v = data.v;
            /* ------------------------ */
            var copyKey = k;
            var copyData = self.__clone(v);

            /* ------------------------ */
            callback.call(_this, copyData, copyKey, self);
        });
    };
    /* ====================================================================== */
    this.some = function(callback, _this) {

        var self = this;
        _this = _this || this;

        var result = false;
        this.dataList.some(function(data, i) {

            var k = data.k;
            var v = data.v;

            var copyKey = k;
            var copyData = self.__clone(v);
            /* ------------------------ */
            result = callback.call(_this, copyData, copyKey, self);

            if (result !== true) {
                result = false;
            }
            /* ------------------------ */
            return result;
        });
        return result;
    };
}).call($Map_.prototype);
////////////////////////////////////////////////////////////////////////////////
(function() {
    this.map = function() {

    };
    /* ====================================================================== */
    this.filter = function() {

    };

}).call($Map_.prototype);

////////////////////////////////////////////////////////////////////////////////
(function() {
    /**
     * 依照(key)排序
     */
    this.keySort = function(sortFun) {
        if (typeof sortFun != 'function') {
            throw new TypeError('sortFun must be function');
        }

        this.dataList.sort(_sortFun);
        /* ---------------------------------- */
        function _sortFun(a, b) {
            // 排序的函式
            var key_a = a.k || undefined;
            var key_b = b.k || undefined;

            return sortFun(key_a, key_b);
        }
    };
    /* ====================================================================== */
    this.valueSort = function(sortFun) {
        if (typeof sortFun != 'function') {
            throw new TypeError('sortFun must be function');
        }
        this.dataList.sort(_sortFun);
        /* ---------------------------------- */
        function _sortFun(a, b) {
            // 排序的函式
            var value_a = a.v || undefined;
            var value_b = b.v || undefined;

            var result = sortFun(value_a, value_b);
            return result;
        }
    };
}).call($Map_.prototype);

////////////////////////////////////////////////////////////////////////////////

(function() {
    /**
     * 複製
     */
    this.slice = function() {
        'use strict';
        // debugger;

        var arg = [].slice.call(arguments);
        var copyDataList = this.dataList.slice.apply(this.dataList, arg);

        var map = new $Map_(copyDataList);

        return map;
    };
    /* ====================================================================== */
    /**
     * 移除,拼接(不能新增)
     */
    this.splice = function(start, count) {
        'use strict';
        // debugger;

        var copyDataList;

        if (arguments.length == 0) {
            copyDataList = this.dataList.splice();
        } else if (arguments.length == 1) {
            copyDataList = this.dataList.splice(start);
        } else {
            // arguments.length > 1
            copyDataList = this.dataList.splice(start, count);
        }

        var map = new $Map_(copyDataList);
        /* ---------------------------------- */
        this.size = this.dataList.length;

        return map;
    };
}).call($Map_.prototype);
////////////////////////////////////////////////////////////////////////////////

(function() {
    this.__clone = function(data) {
        return this.fn.readDataOnly(data);
    };


    this.toJSON = function() {
        var result = [];
        this.dataList.forEach(function(obj) {
            result.push([obj.k, obj.v]);
        });
        return result;
    };

}).call($Map_.prototype);
////////////////////////////////////////////////////////////////////////////////
//
// ($Map_)類別方法
//
// $Map_.readDataOnly(data)
//
// $Map_.makeIterator(array);
//
////////////////////////////////////////////////////////////////////////////////
(function(self) {
    var toString = Object.prototype.toString;
    /**
     * 能複製([]{}Map)三中資料格式
     * ([]{})的表層只能讀取
     */
    self.readDataOnly = function(source) {

        var toString = Object.prototype.toString;
        var clone;
        var type = _getType(source);

        if (/array/gi.test(type) || _isPlainObject(source)) {
            // source is [], {}
            clone = _copyProperty(source);
        } else if (/map/gi.test(type)) {
            // source is Map
            clone = _copyMap(source);
        } else {
            clone = source;
        }
        return clone;
    };
    /* ====================================================================== */
    /**
     * copy([], {})
     */
    function _copyProperty(source) {
        debugger;

        if (!isObject(source)) {
            return source;
        } else {
            var copy;
            var prototype = Object.getPrototypeOf(source);
            /* ---------------------------------- */
            if (typeof prototype != 'undefined') {
                copy = Object.create(prototype);
            } else {
                copy = Object.create(null);
            }
            /* ---------------------------------- */
            copyProperty(copy, source);

            return copy;
        }
        /* =========================================== */
        /**
         * 是否是Object
         */
        function isObject(value) {
            debugger;

            var result = (value === Object(value));
            return result;
        };
        /* =========================================== */

        function copyProperty(copy, source) {
            var propertys = Object.getOwnPropertyNames(source);

            console.dir(propertys);

            propertys.forEach(function(property) {
                var desc = Object.getOwnPropertyDescriptor(source, property);

                // 禁止表層資料被修改新增刪除
                desc.writable = false;
                desc.configurable = false;

                Object.defineProperty(copy, property, desc);
            });
        };
    };
    /* ====================================================================== */
    /**
     * copy map
     */
    function _copyMap(source) {
        var cloneMap = new Map();

        source.forEach(function(v, k) {
            cloneMap.set(k, v);
        });

        return cloneMap;
    };
    /* ====================================================================== */
    /**
     * 確定型別
     */
    function _getType(obj) {
        var type = toString.call(obj) || '';
        type = type.replace(/(^\s?\[object\s?)|(\]\s?$)/gi, '').toLowerCase();
        return type;
    };
    /* ====================================================================== */
    /**
     * 是否是{}
     */
    function _isPlainObject(obj) {
        if (!obj || _getType(obj) !== "object") {
            return false;
        }

        var constructorName = '';
        try {
            constructorName = obj.constructor.toString();
        } catch (error) {}

        // 'Object() {[native code]}'
        if (!/function\s*Object\(\s*\)\s*\{\s*\[native\s*code\]\s*\}\s*$/gi.test(constructorName)) {
            return false;
        }
        return true;
    };
})($Map_);

////////////////////////////////////////////////////////////////////////////////
(function(self) {
    /* ====================================================================== */
    self.makeIterator = function(array) {

        if (!Array.isArray(array)) {
            throw new Error('makeIterator must be array');
        }

        var nextIndex = 0;
        return {
            next: function() {
                return nextIndex < array.length ? {
                    value: array[nextIndex++],
                    done: false
                } : {
                    done: true
                };
            }
        }
    };
})($Map_);