const fs = require('fs');
/**
* @class QKJson
* @name QKJson
* @version 1.3.0
* @author QuKin <13606184008@163.com>
*/
class QKJson {
/**
* 构造函数
* @param {Array|Path|String} [json=[]] JSON
* @constructs
*
* this.json: 原始数据
* this.data: 处理数据 -> where查询过后会赋值给这个
* this.whereTF:是否查询数据
*
* 用法:[{id:0,name:'aaa'},{id:1,name:'bbb'},{id:2,name:'ccc'},{id:3,name:'ddd'}]
*/
constructor(json = []) {
this.set(json);
// 主要用途是where查询存放
this.data = [];
// 判断是否已经执行过where
this.whereTF = false;
}
/**
* 设置JSON数组值
* @param {Array} json JSON数组
*/
set(json) {
if (typeof json === 'string') {
if (json.indexOf('.json') === -1) {
this.json = JSON.parse(json);
} else {
try {
const data = fs.readFileSync(json, 'utf8');
this.json = JSON.parse(data);
} catch (err) {
console.log(err);
}
}
} else {
this.json = json;
}
return this;
}
/**
* 获取值
* @param {Number} [length=0] 获取的长度
* @returns {Array} json数组
*/
get(length = 0) {
if (length === 0) {
if (!this.whereTF) return this.json;
return this.data;
} else {
if (!this.whereTF) return this.json.splice(0, length);
return this.data.splice(0, length);
}
}
/**
* 获取第一个值
* @returns {Array} json数组
*/
first() {
if (!this.whereTF) {
if (this.json.length > 0) return this.json[0];
return [];
} else {
if (this.data.length > 0) return this.data[0];
return [];
}
}
/**
* 获取最后一个值
* @returns {Array} json数组
*/
last() {
if (!this.whereTF) {
if (this.json.length > 0) return this.json[this.json.length - 1];
return [];
} else {
if (this.data.length > 0) return this.data[this.data.length - 1];
return [];
}
}
/**
* 分页
* @param {Number} [pages=0] 查询第几页
* @param {Number} [length=6] 每页的数量
* @returns {this} this
*
* 用法:pagination(0,6)
* 解读:查询第一页,每页的数据条数为6条
*/
pagination(pages = 0, length = 6) {
pages = pages * length;
this.limit(pages, length);
return this;
}
/**
* 限制查询
* @param {Number} [start=0] 从start下标开始
* @param {Number} [end=6] 到end下标结束
* @returns {this} this
*
* 用法:limit(0,6)
*/
limit(start = 0, end = 6) {
if (!this.whereTF) {
this.json = this.json.splice(start, end);
} else {
this.data = this.data.splice(start, end);
}
return this;
}
/**
* 查询单个键名
* @param {String} key 键名
* @param {Number} num 下标
* @returns {this} this
*/
select(key, num = 0) {
if (!this.whereTF) {
this.json = this.json[num][key];
} else {
this.data = this.data[num][key];
}
return this;
}
/**
* 查询指定下标
* @param {Number} num 下标
* @returns {this} this
*/
eq(num) {
if (!this.whereTF) {
if (this.json.length > num) this.json = this.json[num];
} else {
if (this.data.length > num) this.data = this.data[num];
}
return this;
}
/**
* 查询 默认and
* 用到了eval方法,一些游览器不支持
* @param {Array} arr 数组
* @param {String} [join='&&'] 连接。&&:和;||:或者
* @return this
*
* 用法:whereAll([['name','bbb'],['id','<',2]])
* 解读:json.name=="bbb" && json.id<"2"
* 输出:[{id: 1, name: "bbb"}]
*/
whereAll(arr, join = '&&') {
this.whereTF = true;
let temp = [];
let tempArr = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i].length == 2) {
temp.push([arr[i][0], '==', arr[i][1]]);
} else {
temp.push([arr[i][0], arr[i][1], arr[i][2]]);
}
}
this.json.forEach((value) => {
let ifEval = '', a = null, b = null;
for (const item of temp) {
a = !isNaN(Number(value[item[0]])) ? parseInt(value[item[0]]) : '"' + value[item[0]] + '"';
b = !isNaN(Number(item[2])) ? parseInt(item[2]) : '"' + item[2] + '"';
ifEval += a + item[1] + b + ' ' + join + ' ';
}
ifEval = ifEval.substring(0, ifEval.length - 4);
if (eval(ifEval)) {
tempArr.push(value);
}
})
this.data = tempArr;
return this;
}
/**
* 查询
* @param {*} obj1 参数1
* @param {*} [obj2=null] 参数2
* @param {*} [obj2=null] 参数3
* @returns {this} this
*
* 因为js没有重载的概念,所以用arguments来进行判断传入了几个参数
*
* 当只有一个参数时:采用的方法是回调:where((item)=>item.id==1)
* 当只有两个参数时:查询 -> obj1==obj2
* 当有三个参数时,obj2是符号:查询 -> obj1 obj2 obj3
*/
where(obj1, obj2 = null, obj3 = null) {
let arr = [];
if (!this.whereTF) arr = this.json;
else arr = this.data;
this.whereTF = true;
switch (arguments.length) {
case 1:
/**
* @callback obj1 回调
*
* 用法:where((item)=>item.id==1)
* 解读:json.id==1
* 输出:[{id: 1, name: "bbb"}]
*/
this.data = arr.filter(obj1);
break;
case 2:
/**
* @param {*} obj1 键名
* @param {*} obj2 键值
*
* 用法:where('id',1)
* 解读:json.id==1
* 输出:[{id: 1, name: "bbb"}]
*/
this.data = arr.filter((item) => {
return item[obj1] == obj2;
});
break;
case 3:
/**
* @param {*} obj1 键名
* @param {*} obj2 符号,当输入其余的符号,默认是==
* @param {*} obj3 键值
*
* 用法:where('id','===',1)
* 解读:json.id===1
* 输出:[{id: 1, name: "bbb"}]
*/
this.data = arr.filter((item) => {
switch (obj2) {
case '===': return item[obj1] === obj3;
case '<': return item[obj1] < obj3;
case '>': return item[obj1] > obj3;
case '<=': return item[obj1] <= obj3;
case '>=': return item[obj1] >= obj3;
case '!=': return item[obj1] != obj3;
case '!==': return item[obj1] !== obj3;
default: return item[obj1] == obj3;
}
});
break;
}
return this;
}
/**
* 查询,默认为==
* @param {*} key 键名
* @param {*} value 键值
* @returns {this} this
*
* 用法:where('id',1)
* 解读:json.id==1
* 输出:[{id: 1, name: "bbb"}]
*/
whereD(key, value) {
this.whereTF = true;
let arr = [];
if (!this.whereTF) arr = this.json;
else arr = this.data;
this.data = arr.filter((item) => {
return item[key] == value;
});
return this;
}
/**
* 查询
* @param {*} key 键名
* @param {*} symbol 符号,当输入其余的符号,默认是==
* @param {*} value 键值
* @returns {this} this
*
* 用法:where('id','===',1)
* 解读:json.id===1
* 输出:[{id: 1, name: "bbb"}]
*/
whereS(key, symbol, value) {
this.whereTF = true;
let arr = [];
if (!this.whereTF) arr = this.json;
else arr = this.data;
this.data = arr.filter((item) => {
switch (symbol) {
case '===': return item[key] === value;
case '<': return item[key] < value;
case '>': return item[key] > value;
case '<=': return item[key] <= value;
case '>=': return item[key] >= value;
case '!=': return item[key] != value;
case '!==': return item[key] !== value;
default: return item[key] == value;
}
});
return this;
}
/**
* 查询
* @param {Function} callback 回调
* @returns {this} this
* @callback callback 回调
*
* 用法:where((item)=>item.id==1)
* 解读:json.id==1
* 输出:[{id: 1, name: "bbb"}]
*/
whereC(callback) {
this.whereTF = true;
let arr = [];
if (!this.whereTF) arr = this.json;
else arr = this.data;
this.data = arr.filter(callback);
return this;
}
/**
* 插入数据
* @param {JSON} json json
* @returns {this} this
*
* 用法:insert({id:4,name:'eee'})
*/
insert(json) {
if (!this.whereTF) this.json.push(json);
else this.data.push(json);
return this;
}
/**
* 插入键名
* @param {String} key 要新增的键名
* @param {*} [value=null] 新增的键名给它赋值
* @returns {this} this
*
* 用法:insertKey('age',10)
*/
insertKey(key, value = null) {
if (!this.whereTF) {
for (let i = 0; i < this.json.length; i++) this.json[i][key] = value;
} else {
for (let i = 0; i < this.data.length; i++) this.data[i][key] = value;
}
return this;
}
/**
* 修改数据
* @param {String} key 要修改的键名
* @param {*} [value=null] 修改的键名给它赋值
* @returns {this} this
*
* 用法:update('age',12)
*/
update(key, value = null) {
if (!this.whereTF) {
for (let i = 0; i < this.json.length; i++) this.json[i][key] = value;
} else {
for (let i = 0; i < this.data.length; i++) this.data[i][key] = value;
}
return this;
}
/**
* 删除数据
* @returns {Array} json数组
*
* 用法:delete()
* 注意 如delete方法前方没有where进行判断,就会清楚json里面的所有数据;
* 如有就会进行删除json中出现那个值
*/
delete() {
if (!this.whereTF) {
this.json = [];
} else {
this.findDelete(this.json, []);
this.data = [];
}
return this.json;
}
/**
* 查找删除
* 用到了eval方法,一些游览器不支持
* @private
* @param {JSON} json json
* @param {Array} path 路径
*/
findDelete(json, path) {
for (const i in json) {
if (json[i] != null && typeof (json[i]) == "object") this.findDelete(json[i], path.concat(i));
for (let j = 0; j < this.data.length; j++) {
if (JSON.stringify(json[i]) == JSON.stringify(this.data[j])) {
// console.log(json[i], path, i);
let temp = 'this.json'
for (let o = 0; o < path.length; o++) {
temp += '["' + path[o] + '"]'
}
// console.log(temp, eval(temp))
eval(temp).splice(i, 1);
}
}
}
}
/**
* 删除键名
* @param {String} key 要删除的键名
* @returns {this} this
*
* 用法:deleteKey('age')
*/
deleteKey(key) {
if (!this.whereTF) {
for (let i = 0; i < this.json.length; i++) delete this.json[i][key];
} else {
for (let i = 0; i < this.data.length; i++) delete this.data[i][key];
}
return this;
}
/**
* 判断两个json是否相等
* @param {*} json1 json1
* @param {*} json2 json2
*/
equal(objA, objB) {
function isObj(object) {
return object && typeof (object) == 'object' && Object.prototype.toString.call(object).toLowerCase() == "[object object]";
}
function isArray(object) {
return object && typeof (object) == 'object' && object.constructor == Array;
}
if (!isObj(objA) || !isObj(objB)) return false;
if (objA.length != objB.length) return false;
return CompareObj(objA, objB, true);
function CompareObj(objA, objB, flag) {
for (var key in objA) {
if (!flag) //跳出整个循环
break;
if (!objB.hasOwnProperty(key)) {
flag = false;
break;
}
if (!isArray(objA[key])) { //子级不是数组时,比较属性值
if (objB[key] != objA[key]) {
flag = false;
break;
}
} else {
if (!isArray(objB[key])) {
flag = false;
break;
}
var oA = objA[key],
oB = objB[key];
if (oA.length != oB.length) {
flag = false;
break;
}
for (var k in oA) {
if (!flag) //这里跳出循环是为了不让递归继续
break;
flag = CompareObj(oA[k], oB[k], flag);
}
}
}
return flag;
}
}
/**
* 排序
* @param {Array|String} parameters 排序参数
* @param {String} [type='asc'] 排序类型
* @returns {this} this
*
* 用法:orderBy(['age','name'],'desc')
*/
orderBy(parameters, type = 'asc') {
if (typeof parameters == 'string') parameters = [parameters];
function main(arr, parameters) {
let pop = parameters.pop();
arr.sort(function (a, b) {
let convertA = a[pop];
let convertB = b[pop];
if (typeof convertA == 'string' && typeof convertB == 'string') {
if (type == 'asc') return convertA.localeCompare(convertB);
return convertB.localeCompare(convertA);
} else {
if (type == 'asc') return convertA - convertB;
return convertB - convertA;
}
});
if (parameters.length != 0) {
main(arr, parameters);
}
}
if (!this.whereTF) main(this.json, parameters);
else main(this.data, parameters);
return this;
}
/**
* dom导入
* @param {this} self 表单input:file的this指向
*
* 用法:document.getElementById('file').onchange=function(){new QJson().QImport(this)}
*/
domImport(self) {
// 其实是可以扩展到多文件上传的,不过我们就选第一个,也就是下标0
const file = self.files[0];
// !!是一个js的语法,表示后面的变量不是null/undefined/空串,实用写法。
if (!!file) {
// 实例化一个FileReader对象
const reader = new FileReader();
// 借助 FileReader 的方法,按照文本格式读入文件,第二个参数是编码方式(可空)
reader.readAsText(file);
reader.onload = function () {
//然后在FileReader的onload方法里,然后在FileReader的onload方法里,刚刚读入的文件能以文本的形式得到了
this.json = JSON.parse(this.result);
};
}
}
/**
* dom导出
* @param {String} filename 文件名
*/
domExport(filename = 'json') {
let data = [];
if (!this.whereTF) data = this.json;
else data = this.data;
if (!data) {
console.log('没有数据');
}
if (typeof data === "object") {
data = JSON.stringify(data, undefined, 4);
}
// 要创建一个 blob 数据
let blob = new Blob([data], { type: "text/json" }),
a = document.createElement("a");
a.download = filename + '.json';
// 将blob转换为地址
// 创建 URL 的 Blob 对象
a.href = window.URL.createObjectURL(blob);
// 标签 data- 嵌入自定义属性 屏蔽后也可正常下载
a.dataset.downloadurl = ["text/json", a.download, a.href].join(":");
// 添加鼠标事件
let event = new MouseEvent("click", {});
// 向一个指定的事件目标派发一个事件
a.dispatchEvent(event);
}
/**
* node导出
* @param {String} filename 文件名
*/
export(filename = 'json') {
// let data = '';
// if (!this.whereTF) data = JSON.stringify(this.json);
// else data = JSON.stringify(this.data);
if (filename.indexOf('.json') === -1) filename += '.json';
fs.writeFileSync(filename, JSON.stringify(this.json));
}
/**
* 内连接
* (不支持json有子节点的内的内连接)
* @param {JSON} json json数据
* @param {String|Array} key 共同的key值
* @returns {this} this
*/
innerJoin(json, key) {
this.whereTF = true;
this.data = [];
// 获取两个json所有的key值,并去重
let keys = [...new Set(Object.keys(this.json[0]).concat(Object.keys(json[0])))];
for (let i = 0; i < this.json.length; i++) {
for (let j = 0; j < json.length; j++) {
// 判断传进来的key值是字符串还是数组
if (typeof key === 'string') {
if (this.json[i][key] === json[j][key]) {
let temp = {};
for (let o = 0; o < keys.length; o++) {
temp[keys[o]] = this.json[i][keys[o]] === undefined ? json[j][keys[o]] : this.json[i][keys[o]];
}
this.data.push(temp);
}
} else if (typeof key === 'object') {
let ifEval = '';
for (let k = 0; k < key.length; k++) {
ifEval += 'this.json[i]["' + key[k] + '"]===json[j]["' + key[k] + '"] && ';
}
// 主要原因就是到最后会多出来:' && ',这个字符,就多加一个判断,也能用splice等其余操作去掉
ifEval += '1===1';
if (eval(ifEval)) {
let temp = {};
for (let o = 0; o < keys.length; o++) {
temp[keys[o]] = this.json[i][keys[o]] === undefined ? json[j][keys[o]] : this.json[i][keys[o]];
}
this.data.push(temp);
}
}
}
}
return this;
}
/**
* 左连接
* (不支持json有子节点的内的左连接)
* @param {JSON} json json数据
* @param {String|Array} key 共同的key值
* @returns {this} this
*/
leftJoin(json, key) {
this.whereTF = true;
this.data = [];
let keys = [...new Set(Object.keys(this.json[0]).concat(Object.keys(json[0])))];
for (let i = 0; i < this.json.length; i++) {
// 因为左连接,this.json为主,json为辅,一个循环判断下来就是一个
let temp = {};
for (let j = 0; j < json.length; j++) {
if (typeof key === 'string') {
if (this.json[i][key] === json[j][key]) {
for (let o = 0; o < keys.length; o++) {
temp[keys[o]] = this.json[i][keys[o]] === undefined ? json[j][keys[o]] : this.json[i][keys[o]];
}
// 如果不退出,就会存入空的字符
break;
} else {
// 这里没有break是因为有可能会出现null,如果循环一遍都没有,就是null
for (let o = 0; o < keys.length; o++) {
temp[keys[o]] = this.json[i][keys[o]] === undefined ? null : this.json[i][keys[o]];
}
}
} else if (typeof key === 'object') {
let ifEval = '';
for (let k = 0; k < key.length; k++) {
ifEval += 'this.json[i]["' + key[k] + '"]===json[j]["' + key[k] + '"] && ';
}
ifEval += '1===1';
if (eval(ifEval)) {
for (let o = 0; o < keys.length; o++) {
temp[keys[o]] = this.json[i][keys[o]] === undefined ? json[j][keys[o]] : this.json[i][keys[o]];
}
break;
} else {
for (let o = 0; o < keys.length; o++) {
temp[keys[o]] = this.json[i][keys[o]] === undefined ? null : this.json[i][keys[o]];
}
}
}
}
this.data.push(temp);
}
return this;
}
/**
* 右连接
* (不支持json有子节点的内的右连接)
* @param {JSON} json json数据
* @param {String|Array} key 共同的key值
* @returns {this} this
*/
rightJoin(json, key) {
this.whereTF = true;
this.data = [];
let keys = [...new Set(Object.keys(this.json[0]).concat(Object.keys(json[0])))];
for (let i = 0; i < json.length; i++) {
let temp = {};
for (let j = 0; j < this.json.length; j++) {
if (typeof key === 'string') {
if (json[i][key] === this.json[j][key]) {
for (let o = 0; o < keys.length; o++) {
temp[keys[o]] = json[i][keys[o]] === undefined ? this.json[j][keys[o]] : json[i][keys[o]];
}
break;
} else {
for (let o = 0; o < keys.length; o++) {
temp[keys[o]] = json[i][keys[o]] === undefined ? null : json[i][keys[o]];
}
}
} else if (typeof key === 'object') {
let ifEval = '';
for (let k = 0; k < key.length; k++) {
ifEval += 'json[i]["' + key[k] + '"]===this.json[j]["' + key[k] + '"] && ';
}
ifEval += '1===1';
if (eval(ifEval)) {
for (let o = 0; o < keys.length; o++) {
temp[keys[o]] = json[i][keys[o]] === undefined ? this.json[j][keys[o]] : json[i][keys[o]];
}
break;
} else {
for (let o = 0; o < keys.length; o++) {
temp[keys[o]] = json[i][keys[o]] === undefined ? null : json[i][keys[o]];
}
}
}
}
this.data.push(temp);
}
return this;
}
/**
* 完全连接
* (不支持json有子节点的内的完全连接)
* @param {JSON} json json数据
* @param {String|Array} key 共同的key值
* @returns {this} this
*/
fullJoin(json, key) {
this.whereTF = true;
this.data = [];
let temp = [];
this.leftJoin(json, key);
temp.push(...this.data);
this.rightJoin(json, key);
temp.push(...this.data);
temp = this.unRepeat(temp, key);
this.data = temp;
return this;
}
/**
* json去重
* @param {Array} arr json数组
* @param {String} key key值
* @returns {Array} json数组
*/
unRepeat(arr, key) {
const res = new Map();
return arr.filter((arr) => !res.has(arr[key]) && res.set(arr[key], 1));
}
/**
* 清除缓存
* @returns {this} this
*
* 用途:where从头开始和清除data缓存
*/
clear() {
this.whereTF = false;
this.data = [];
return this;
}
/**
* 关闭
* @returns {Boolean} true
*/
close() {
this.json = [];
this.data = [];
this.whereTF = [];
return true;
}
}