/** @module Collection */ kit.PluginManager.register('Collection', function(module, exports, require, global) { var support = require('support'); var valueEx = support.valueEx; function filter(value, key, arr) { return !support.empty(value); } function safeArrayKey(key){ return support.arr.isKey(key)?Number(key):(''+key); } /** * @param type * @return {kit.Collection} */ function getSimilarInstance(type) { if(type === 'object') { return new Collection({}); } return new Collection([]); } /** * @class Collection * @property length * @memberOf kit */ var Collection = function (data = []) { var self = this; this.data = data; support.definePropertyGet(this, 'length', function () { return self.__keys.length; }); if(data instanceof Collection) { this.__type = data.__type; this.__keys = data.__keys; this.__values = data.__values; return; } if(Array.isArray(data)) { this.__type = 'array'; } else if(typeof data === 'object') { this.__type = 'object'; if(data.hasOwnProperty('length') && !Object.keys(data).includes('length')) { this.__type = 'arrayObject'; } } else { this.__type = 'array'; this.data = [data]; console.warn('collection: not support data' + typeof data); } var key; this.__keys = []; this.__values = []; for(key in this.data) { if(!this.data.hasOwnProperty(key)) continue; this.__keys.push(safeArrayKey(key)); this.__values.push(this.data[key]); } }; /** * @private */ Collection.prototype.__getLastIndex = function () { var lastIndex = -1, n = 0, index; for(index in this.__keys) { n = this.__keys[index]; if (support.isNumber(n) && n > lastIndex) { lastIndex = n; } } return lastIndex; }; Collection.prototype.all = function () { if(this.__type === 'array' || this.__type === 'arrayObject') { return this.toArray(); } return this.toObject(); }; Collection.prototype.toArray = function () { var index, arr = []; for(index in this.__keys) { arr.push(this.__values[index]); } return arr; }; Collection.prototype.toObject = function () { var index, ob = {}; for(index in this.__keys) { ob[this.__keys[index]] = this.__values[index]; } return ob; }; Collection.prototype.serialize = function () { return JSON.stringify(this.toObject()); }; Collection.prototype.serializeArray = function () { return JSON.stringify(this.toArray()); }; Collection.prototype.has = function (key) { return this.__keys.includes(safeArrayKey(key)); }; Collection.prototype.indexKey = function (key) { return this.__keys.indexOf(safeArrayKey(key)); }; /** * @param key * @param value * @return {kit.Collection} */ Collection.prototype.add = Collection.prototype.set = Collection.prototype.put = function (key, value) { var index = -1; if(this.has(key)) { this.data[key] = this.__values[index = this.indexKey(key)] = value; return this; } this.length = this.__keys.push(safeArrayKey(key)); this.data[key] = this.__values[this.length-1] = value; return this; }; /** * @param value * @return {kit.Collection} */ Collection.prototype.push = function (value) { this.set(this.__getLastIndex() + 1, value); return this; }; Collection.prototype.get = function (key, defValue = undefined) { if(this.has(key)) { return this.data[key]; } return defValue; }; Collection.prototype.count = function () { return this.__keys.length; }; Collection.prototype.isEmpty = function () { return this.count() === 0; }; /** * возвращает новый экземпляр коллекции; не изменяет вызываемую коллекцию * * @param callback * @return {kit.Collection} */ Collection.prototype.map = function (callback) { var res = getSimilarInstance(this.__type); var index, key, value; for(index in this.__keys) { key = this.__keys[index]; value = this.__values[index]; res.put(key, valueEx(callback, value, key, this.data)); } return res; }; /** * @param callback * @return {kit.Collection} */ Collection.prototype.each = function (callback = true) { var i; if(this.__keys.length) for (i = 0; i < this.__keys.length; i++) { if(valueEx(callback, this.__values[i], this.__keys[i], this.data) === false) { break; } } return this; }; /** * @param callback * @return {kit.Collection} */ Collection.prototype.eachReverse = function (callback = true) { var i; if(this.__keys.length) for (i = this.__keys.length - 1; i >= 0; i--) { if(valueEx(callback, this.__values[i], this.__keys[i], this.data) === false) { break; } } return this; }; /** * @param callback * @return {kit.Collection} */ Collection.prototype.filter = function (callback = filter) { var res = getSimilarInstance(this.__type); this.each(function (value, key, arr) { if(!!valueEx(callback, value, key, arr)) { res.put(key, value); } }); return res; }; /** * Метод возвращает все элементы коллекции, кроме тех у кого ключи соответствуют заданным * @param keys * @return {kit.Collection} */ Collection.prototype.except = function (keys) { var __keys = [], iKey; if(!Array.isArray(keys)) { keys = [keys]; } for(iKey in keys) { __keys.push(safeArrayKey(keys[iKey])); } return this.filter(function (value, key) { return !__keys.includes(key); }); }; /** * Метод возвращает элементы коллекции с заданными ключами * @param keys * @return {kit.Collection} */ Collection.prototype.only = function (keys) { var __keys = [], iKey; if(!Array.isArray(keys)) { keys = [keys]; } for(iKey in keys) { __keys.push(safeArrayKey(keys[iKey])); } return this.filter(function (value, key) { return __keys.includes(key); }); }; Collection.prototype.every = function (callback) { var res = true; this.each(function (value, key, arr) { res = res && !!valueEx(callback, value, key, arr); }); return res; }; Collection.prototype.first = function (callback = true) { var res = undefined; this.each(function (value, key, arr) { if(valueEx(callback, value, key, arr)) { res = value; return false; } }); return res; }; Collection.prototype.firstKey = function (callback = true) { var res = undefined; this.each(function (value, key, arr) { if(valueEx(callback, value, key, arr)) { res = key; return false; } }); return res; }; Collection.prototype.last = function (callback = true) { var res = undefined; this.eachReverse(function (value, key, arr) { if(valueEx(callback, value, key, arr)) { res = value; return false; } }); return res; }; Collection.prototype.lastKey = function (callback = true) { var res = undefined; this.eachReverse(function (value, key, arr) { if(valueEx(callback, value, key, arr)) { res = key; return false; } }); return res; }; /** * Method removes/delete an item from the collection by its key * @param key * @return {kit.Collection} */ Collection.prototype.forget = function (key) { key = valueEx(key); if(!this.has(key)) { return this; } var index = this.indexKey(key); this.__keys.splice(index, 1); this.__values.splice(index, 1); delete this.data[key]; return this; }; /** * Method removes and returns an item from the collection by its key * @param key * @param defValue * @return {kit.Collection} */ Collection.prototype.pull = function (key, defValue = undefined) { key = valueEx(key); if(!this.has(key)) { return defValue; } var index = this.indexKey(key); var value = this.__values[index]; this.__keys.splice(index, 1); this.__values.splice(index, 1); delete this.data[key]; return value; }; /** * Method removes and returns the last item from the collection */ Collection.prototype.pop = function(){ var index = this.__keys.length - 1; if(index < 0) { return undefined; } var key = this.__keys[index]; var value = this.__values[index]; this.__keys.splice(index, 1); this.__values.splice(index, 1); delete this.data[key]; return value; }; /** * Method removes and returns the first item from the collection */ Collection.prototype.shift = function(){ var index = 0; if(this.length <= 0) { return undefined; } var key = this.__keys[index]; var value = this.__values[index]; this.__keys.splice(index, 1); this.__values.splice(index, 1); delete this.data[key]; return value; }; /** * @return {kit.Collection} */ Collection.prototype.keys = function () { return new Collection(Object.keys(this.data)); }; /** * @return {kit.Collection} */ Collection.prototype.dump = function () { console.log('Collection', this.data); return this; }; /** * @return {kit.Collection} */ Collection.prototype.dd = function () { this.dump(); debugger; return this; }; /** * @return {kit.Collection} */ Collection.prototype.eachCall = function (context, args1, args2) { var args = [], i; if(arguments.length > 1) for(i = 1; arguments.length - 1 >= i;i++) { args.push(arguments[i]); } if(this.__keys.length) for (i = 0; i < this.__keys.length; i++) { if(typeof this.__values[i] === 'function' && this.__values[i].apply(context, args) === false) { break; } } return this; }; /** * аналог Array.includes * * @param searchElement * @param fromIndex * @return {boolean} */ Collection.prototype.includes = function(searchElement, fromIndex) { var len = this.__keys.length; if (len === 0) { return false; } var n = fromIndex | 0; var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); function sameValueZero(x, y) { return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)); } while (k < len) { if (sameValueZero(this.__values[k], searchElement)) { return true; } k++; } return false; }; Collection.prototype.search = function(searchElement) { var len = this.__keys.length; if (len === 0) { return false; } var k = 0; function sameValueZero(x, y) { return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)); } while (k < len) { if (sameValueZero(this.__values[k], searchElement)) { return this.__keys[k]; } k++; } return false; }; /** * Метод извлекает все значения для данного ключа * ключи оставляет исходные * Значения в коллекци должны быть массиво подобные * * @param key * @return {kit.Collection} */ Collection.prototype.pluck = function (key) { return this.map(function (value) { if(value instanceof Object && value.hasOwnProperty(key)){ return value[key]; } return undefined; }); }; return module.exports = Collection; });