Created
January 4, 2015 09:45
-
-
Save janryWang/c898c163134f63379f8f to your computer and use it in GitHub Desktop.
一个数据处理器,api语法类似mongodb,可以对json任意层次的数据进行单元化克隆,混合,数组操作,查询排序等!功能非常强大
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var dataOperator = (function() { | |
var COMMANDS, | |
DataOperator, | |
KEYWORDS, | |
bind, | |
copy, | |
forEach, | |
getType, | |
inPaths, | |
isArray, | |
isBoolean, | |
isEmptyArray, | |
isEmptyObj, | |
isFunction, | |
isObject, | |
isReference, | |
isString, | |
isValue, | |
merge, | |
toArray, | |
__slice = [].slice; | |
COMMANDS = { | |
OPERATORS: "$remove,$set,$push,$slice,$concat,$pop,$unshift,$merge,$deep_merge,$find,$sort,$foreach".split(","), | |
FILTERS: "$gt,$lt,$is,$gte,$lte,$icontains,$contains,$in,$not_in".split(","), | |
SORTS: "$desc,$asc".split(","), | |
CLONES: "$white_list,$black_list,$filter,$deep".split(","), | |
COMMONS: "$callback".split(",") | |
}; | |
KEYWORDS = (function() { | |
var cmd_name, | |
cmds_name, | |
keywords, | |
_i, | |
_len, | |
_ref; | |
keywords = {}; | |
for (cmds_name in COMMANDS) { | |
_ref = COMMANDS[cmds_name]; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
cmd_name = _ref[_i]; | |
keywords[cmd_name] = true; | |
} | |
} | |
return keywords; | |
})(); | |
getType = function(val) { | |
return Object.prototype.toString.call(val); | |
}; | |
isArray = function(arr) { | |
return getType(arr) === "[object Array]"; | |
}; | |
isEmptyArray = function(arr) { | |
return isArray(arr) && arr.length === 0; | |
}; | |
toArray = function() { | |
var args; | |
args = [].slice.apply(arguments); | |
return [].slice.apply(args[0], args.slice(1)); | |
}; | |
isString = function(str) { | |
return getType(str) === "[object String]"; | |
}; | |
isFunction = function(fun) { | |
return getType(fun) === "[object Function]"; | |
}; | |
isObject = function(obj) { | |
return getType(obj) === "[object Object]"; | |
}; | |
isReference = function(val) { | |
return isArray(val) || isObject(val); | |
}; | |
isEmptyObj = function(obj) { | |
return ! (isReference(obj) && Object.keys(obj).length > 0); | |
}; | |
isBoolean = function(obj) { | |
return getType(obj) === "[object Boolean]"; | |
}; | |
isValue = function(val) { | |
return ! isReference(val); | |
}; | |
bind = function() { | |
var args, | |
context, | |
fun; | |
fun = arguments[0], | |
context = arguments[1], | |
args = 3 <= arguments.length ? __slice.call(arguments, 2) : []; | |
return function() { | |
var res; | |
if (isFunction(fun) && (res = fun.apply(context, args))) { | |
return res; | |
} else {} | |
}; | |
}; | |
forEach = function(arr, callback) { | |
var index, | |
keys, | |
keys_length, | |
_results; | |
if (!isFunction(callback)) { | |
return; | |
} | |
if (isReference(arr)) { | |
keys = Object.keys(arr); | |
keys_length = keys.length; | |
index = 0; | |
_results = []; | |
while (index < keys_length) { | |
if (callback(arr[keys[index]], keys[index]) === false) { | |
break; | |
} | |
_results.push(index++); | |
} | |
return _results; | |
} | |
}; | |
merge = function() { | |
var args, | |
args_length, | |
isDeep; | |
args = toArray(arguments); | |
isDeep = false; | |
if (isBoolean(args[0])) { | |
isDeep = args[0] === true ? true: false; | |
args = args.slice(1); | |
} | |
args_length = args.length; | |
if (args_length < 2) { | |
return args[0]; | |
} | |
if (args_length === 2) { | |
forEach(args[1], | |
function(item, name) { | |
args[0][name] = args[0][name] || args[1][name].constructor(); | |
if (isDeep && isReference(item)) { | |
merge(isDeep, args[0][name], item); | |
} else { | |
args[0][name] = item; | |
} | |
}); | |
return args[0]; | |
} else { | |
return merge(isDeep, args[0], merge.apply(null, [isDeep].concat(args.slice(1)))); | |
} | |
}; | |
copy = function() { | |
var args, | |
filter, | |
isDeep, | |
objType, | |
path, | |
res; | |
args = toArray(arguments); | |
isDeep = false; | |
if (isBoolean(args[0])) { | |
isDeep = args[0] === true ? true: false; | |
args = args.slice(1); | |
} | |
if (isValue(args[0])) { | |
return args[0]; | |
} | |
filter = args[1] && isFunction(args[1]) ? args[1] : false; | |
path = args[2] && isString(args[2]) ? args[2] : ""; | |
objType = isArray(args[0]) ? 1: 2; | |
res = args[0].constructor(); | |
forEach(args[0], | |
function(item, name) { | |
var filter_res, | |
_path; | |
_path = path ? (objType === 2 ? path + "." + name: path + "[" + name + "]") : name; | |
if (filter) { | |
filter_res = filter(item, name, _path); | |
if (isBoolean(filter_res)) { | |
if (filter_res) { | |
if (isDeep && isReference(item)) { | |
res[name] = copy(isDeep, item, filter, _path); | |
_path = path; | |
} else { | |
res[name] = item; | |
} | |
} else { | |
res[name] = item; | |
} | |
} else { | |
if (isDeep && isReference(item)) { | |
res[name] = copy(isDeep, item, filter, _path); | |
_path = path; | |
} else { | |
res[name] = item; | |
} | |
} | |
} else { | |
if (isDeep && isReference(item)) { | |
res[name] = copy(isDeep, item, filter, _path); | |
_path = path; | |
} else { | |
res[name] = item; | |
} | |
} | |
}); | |
return res; | |
}; | |
inPaths = function(path, paths, _swap) { | |
var res, | |
_inPath; | |
res = false; | |
_inPath = function(a, b) { | |
var a_l, | |
b_l, | |
index; | |
a_l = a.length; | |
b_l = b.length; | |
index = 0; | |
while (index < b_l) { | |
if (b.charAt(index) !== a.charAt(index)) { | |
return false; | |
} | |
index++; | |
} | |
if (index === b_l) { | |
if (a_l === b_l) { | |
return true; | |
} else if (a_l > b_l && "[.".indexOf(a.charAt(b_l)) > -1) { | |
return true; | |
} | |
} | |
}; | |
forEach(paths, | |
function(_path) { | |
res = _swap ? _inPath(_path, path) : _inPath(path, _path); | |
if (res) { | |
return false; | |
} | |
}); | |
return res; | |
}; | |
DataOperator = (function() { | |
function DataOperator(data, cmds) { | |
this.data = data; | |
this._commands.__parent__ = this; | |
this._filters.__parent__ = this; | |
if (cmds) { | |
this.exec(cmds); | |
} | |
} | |
DataOperator.prototype._collect = function(obj) { | |
var name, | |
res; | |
res = { | |
operators: {}, | |
filters: {}, | |
sorts: {}, | |
clones: {}, | |
commons: {}, | |
props: {}, | |
value: null | |
}; | |
if (!isReference(obj)) { | |
res.value = obj; | |
return res; | |
} | |
for (name in obj) { | |
if (COMMANDS.OPERATORS.indexOf(name) > -1) { | |
res.operators[name] = obj[name]; | |
} else if (COMMANDS.FILTERS.indexOf(name) > -1) { | |
res.filters[name] = obj[name]; | |
} else if (COMMANDS.SORTS.indexOf(name) > -1) { | |
res.sorts[name] = obj[name]; | |
} else if (COMMANDS.CLONES.indexOf(name) > -1) { | |
res.clones[name] = obj[name]; | |
} else if (COMMANDS.COMMONS.indexOf(name) > -1) { | |
res.commons[name] = obj[name]; | |
} else { | |
res.props[name] = obj[name]; | |
} | |
} | |
return res; | |
}; | |
DataOperator.prototype._filters = { | |
$is: function(a, b) { | |
return a === b; | |
}, | |
$gt: function(a, b) { | |
return a > b; | |
}, | |
$lt: function(a, b) { | |
return a < b; | |
}, | |
$gte: function(a, b) { | |
return a >= b; | |
}, | |
$lte: function(a, b) { | |
return a <= b; | |
}, | |
$icontains: function(a, b) { | |
return a.toLowerCase().indexOf(b.toLowerCase()) > -1; | |
}, | |
$contains: function(a, b) { | |
return a.indexOf(b) > -1; | |
}, | |
$in: function(a, b) { | |
return b.indexOf(a) > -1; | |
}, | |
$not_in: function(a, b) { | |
return b.indexOf(a) === -1; | |
} | |
}; | |
DataOperator.prototype._commands = { | |
$set: function(ref, collected) { | |
var prop_name, | |
prop_val, | |
_ref, | |
_results; | |
_ref = collected.props; | |
_results = []; | |
for (prop_name in _ref) { | |
prop_val = _ref[prop_name]; | |
if (isReference(prop_val)) { | |
ref[prop_name] = ref[prop_name] || prop_val.constructor(); | |
_results.push(this.$set(ref[prop_name], this.__parent__._collect(prop_val))); | |
} else { | |
_results.push(ref[prop_name] = prop_val); | |
} | |
} | |
return _results; | |
}, | |
$remove: function(ref, collected) { | |
var prop_name, | |
prop_val, | |
_ref, | |
_results; | |
_ref = collected.props; | |
_results = []; | |
for (prop_name in _ref) { | |
prop_val = _ref[prop_name]; | |
if (!isArray(ref) && prop_val === true) { | |
_results.push(delete ref[prop_name]); | |
} else { | |
_results.push(void 0); | |
} | |
} | |
return _results; | |
}, | |
$slice: function(ref, collected) { | |
var prop_name, | |
prop_val, | |
_ref, | |
_results; | |
_ref = collected.props; | |
_results = []; | |
for (prop_name in _ref) { | |
prop_val = _ref[prop_name]; | |
if (ref[prop_name] && isArray(ref[prop_name])) { | |
if (!isArray(prop_val)) { | |
prop_val = [prop_val]; | |
} | |
_results.push(ref[prop_name] = ref[prop_name].slice.apply(ref[prop_name], prop_val)); | |
} else { | |
_results.push(void 0); | |
} | |
} | |
return _results; | |
}, | |
$push: function(ref, collected) { | |
var prop_name, | |
prop_val, | |
_ref, | |
_results; | |
_ref = collected.props; | |
_results = []; | |
for (prop_name in _ref) { | |
prop_val = _ref[prop_name]; | |
if (ref[prop_name] && isArray(ref[prop_name])) { | |
_results.push(ref[prop_name].push(prop_val)); | |
} else { | |
_results.push(void 0); | |
} | |
} | |
return _results; | |
}, | |
$concat: function(ref, collected) { | |
var prop_name, | |
prop_val, | |
_ref, | |
_results; | |
_ref = collected.props; | |
_results = []; | |
for (prop_name in _ref) { | |
prop_val = _ref[prop_name]; | |
if (ref[prop_name] && isArray(ref[prop_name])) { | |
if (!isArray(prop_val)) { | |
prop_val = [prop_val]; | |
} | |
_results.push(ref[prop_name] = ref[prop_name].concat(prop_val)); | |
} else { | |
_results.push(void 0); | |
} | |
} | |
return _results; | |
}, | |
$pop: function(ref, collected) { | |
var prop_name, | |
prop_val, | |
_ref, | |
_results; | |
_ref = collected.props; | |
_results = []; | |
for (prop_name in _ref) { | |
prop_val = _ref[prop_name]; | |
if (ref[prop_name] && isArray(ref[prop_name])) { | |
_results.push(ref[prop_name].pop()); | |
} else { | |
_results.push(void 0); | |
} | |
} | |
return _results; | |
}, | |
$unshift: function(ref, collected) { | |
var prop_name, | |
prop_val, | |
_ref, | |
_results; | |
_ref = collected.props; | |
_results = []; | |
for (prop_name in _ref) { | |
prop_val = _ref[prop_name]; | |
if (ref[prop_name] && isArray(ref[prop_name])) { | |
_results.push(ref[prop_name].unshift(prop_val)); | |
} else { | |
_results.push(void 0); | |
} | |
} | |
return _results; | |
}, | |
$merge: function(ref, collected) { | |
return merge(ref, collected.props); | |
}, | |
$deep_merge: function(ref, collected) { | |
return merge(true, ref, collected.props); | |
}, | |
$clone: function(ref, collected) { | |
var blackList, | |
callback, | |
filter, | |
isDeep, | |
res, | |
whiteList; | |
isDeep = !!collected.clones.$deep; | |
whiteList = collected.clones.$white_list; | |
blackList = collected.clones.$black_list; | |
filter = collected.clones.$filter; | |
callback = collected.commons.$callback; | |
if (!isEmptyArray(whiteList)) { | |
res = copy(isDeep, ref, | |
function(item, name, path) { | |
var filter_res; | |
if (inPaths(path, whiteList, true)) { | |
if (!isEmptyArray(blackList)) { | |
if (!inPaths(path, blackList)) { | |
if (isFunction(filter)) { | |
filter_res = filter(item, name, path); | |
} | |
if (isBoolean(filter_res)) { | |
if (filter_res) { | |
return true; | |
} else { | |
return false; | |
} | |
} else { | |
return true; | |
} | |
} else { | |
return false; | |
} | |
} else { | |
return true; | |
} | |
} else { | |
return false; | |
} | |
}); | |
} else if (!isEmptyArray(blackList)) { | |
res = copy(isDeep, ref, | |
function(item, name, path) { | |
var filter_res; | |
if (!inPaths(path, blackList)) { | |
if (isFunction(filter)) { | |
filter_res = filter(item, name, path); | |
} | |
if (isBoolean(filter_res)) { | |
if (filter_res) { | |
return true; | |
} else { | |
return false; | |
} | |
} else { | |
return true; | |
} | |
} else { | |
return false; | |
} | |
}); | |
} else if (isFunction(filter)) { | |
res = copy(isDeep, ref, | |
function(item, name, path) { | |
var filter_res; | |
if (isFunction(filter)) { | |
filter_res = filter(item, name, path); | |
} | |
if (isBoolean(filter_res)) { | |
if (filter_res) { | |
return true; | |
} else { | |
return false; | |
} | |
} else { | |
return true; | |
} | |
}); | |
} else { | |
res = copy(isDeep, ref); | |
} | |
if (isFunction(callback)) { | |
return callback(res); | |
} | |
}, | |
$find: function(ref, collected) { | |
var callback, | |
condition_length, | |
ok, | |
res, | |
_this; | |
_this = this; | |
callback = collected.commons.$callback; | |
ok = 0; | |
condition_length = Object.keys(collected.filters).length; | |
res = []; | |
if (!isEmptyObj(collected.filters)) { | |
forEach(ref, | |
function(ref_item) { | |
if (!isEmptyObj(ref_item)) { | |
forEach(collected.filters, | |
function(filter_body, filter_name) { | |
return forEach(filter_body, | |
function(item, key) { | |
if (_this.__parent__._filters[filter_name](ref_item[key], item)) { | |
return ok++; | |
} | |
}); | |
}); | |
if (ok >= condition_length) { | |
res.push(ref_item); | |
return false; | |
} | |
} | |
}); | |
} else { | |
res = ref; | |
} | |
if (isFunction(callback)) { | |
return callback(res); | |
} | |
}, | |
$foreach: function(ref, collected) { | |
if (isFunction(collected.value)) { | |
return forEach(ref, collected.value); | |
} | |
}, | |
$sort: function(ref, collected) { | |
var key, | |
val, | |
_ref, | |
_results; | |
if (isArray(ref) && isReference(collected.value)) { | |
_ref = collected.props; | |
_results = []; | |
for (key in _ref) { | |
val = _ref[key]; | |
if (val === "desc") { | |
_results.push(ref.sort(function(a, b) { | |
if (isReference(a) && isReference(b)) { | |
if (a[key] < b[key]) { | |
return 1; | |
} else { | |
return - 1; | |
} | |
} else { | |
if (a < b) { | |
return 1; | |
} else { | |
return - 1; | |
} | |
} | |
})); | |
} else { | |
_results.push(ref.sort(function(a, b) { | |
if (isReference(a) && isReference(b)) { | |
if (a[key] > b[key]) { | |
return 1; | |
} else { | |
return - 1; | |
} | |
} else { | |
if (a > b) { | |
return 1; | |
} else { | |
return - 1; | |
} | |
} | |
})); | |
} | |
} | |
return _results; | |
} else { | |
if (collected.value === "desc") { | |
return ref.sort(function(a, b) { | |
return b - a; | |
}); | |
} else { | |
return ref.sort(function(a, b) { | |
return a - b; | |
}); | |
} | |
} | |
} | |
}; | |
DataOperator.prototype._exec = function(ref, cmd_obj) { | |
var cmd_body, | |
cmd_name, | |
_results; | |
_results = []; | |
for (cmd_name in cmd_obj) { | |
cmd_body = cmd_obj[cmd_name]; | |
if (this._commands[cmd_name]) { | |
_results.push(this._commands[cmd_name](ref, this._collect(cmd_body))); | |
} else if (isReference(cmd_body) && !KEYWORDS[cmd_name]) { | |
ref[cmd_name] = ref[cmd_name] || cmd_body.constructor(); | |
_results.push(this._exec(ref[cmd_name], cmd_body)); | |
} else { | |
_results.push(void 0); | |
} | |
} | |
return _results; | |
}; | |
DataOperator.prototype.exec = function(cmds) { | |
return this._exec(this.data, cmds); | |
}; | |
return DataOperator; | |
})(); | |
merge(DataOperator, { | |
exec: function(data, cmds) { | |
return new DataOperator(data, cmds); | |
}, | |
isArray: isArray, | |
isString: isString, | |
isFunction: isFunction, | |
isObject: isObject, | |
isReference: isReference, | |
isEmptyObj: isEmptyObj, | |
isEmptyArray: isEmptyArray, | |
isValue: isValue, | |
bind: bind, | |
forEach: forEach, | |
merge: merge, | |
copy: copy | |
}); | |
return DataOperator; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
下面是使用方法:
如果给定一个测试对象:
test_obj = {
name:"janry",
.....
};
如果要很简单的克隆只需要DataOperator.copy(test_obj)就行了,当然这是浅复制,
如果要深度复制DataOperator.copy(true,test_obj),这就是深度复制了,
如果你的需求有点变态,需要对于某个属性深度复制,其他属性则浅复制
DataOperator.copy(true,test_obj,function(value/属性值/,key/属性键名/,path/属性所在路径/){
if(condition){
return true;//说明深度复制
} else {
return false;//说明浅复制
}
});
这是对于静态方法的使用。
下面则是对于动态方法的使用
DataOperator.exec(test_obj,{
$set:{age:23}
});这种方式相当于给对象添加一个属性,其他类似的方法还有$remove,$push,$slice,$concat,$pop,$unshift,$merge,$deep_merge,$find,$sort,$foreach