最近有个需求要求比较json数据,当然有最简单的方法,循环每个键来比值,但是我偏不!因为正好想趁这个机会写一个文本比较的工具(类似git自带的比较),我搜了一下网络只找到一个LD算法似乎可以用,自己写了个demo效率一般把,而且感觉好复杂太高深。所以还是以我的理解写了一个比较工具。
水平有限所以我不会写很高深的东西,所以尽量往简单的地方靠。基本概念,就是先把文本分段(比如5行一段),然后在这5行里找一个连续最多的行做基准(文本差异度>60的认为是新增行,其他的是编辑行),向下连续查找。简单的说就是尽量把能连续对上的作为基准,一路往下对。
这里提一下,如果希望比对的更精确,可以把findLen设置的大一点,比如设置成left或right里比较大的行数,这样比对起来会避免一些问题,如:当中隔了很多行(>findLen)才是正确的行,导致检索默认5行没找到,就认为是新增行了。当然这样每条比的数量就多了也就慢了自己把握。
function compare(){
this.findLen = 5, // 查块大小,如果你希望更准确可以把这个值改大一点比如50行
this.jsonSort = function (obj) {
let names = [];
let newObj = {};
if (Array.isArray(obj)) {
let list = [];
for (let i=0;i<obj.length;i++) {
list.push(this.jsonSort(obj[i]));
}
list.sort();
return list;
} else {
for (let o in obj) {
names.push(o);
}
names.sort();
for (let i=0;i<names.length;i++) {
let n = names[i];
if ("function" != typeof obj[n]) {
if ("object" == typeof obj[n]) {
if (obj[n]) {
newObj[n] = this.jsonSort(obj[n]);
}
} else {
newObj[n] = obj[n];
}
}
}
}
return newObj;
},
this.json2Lines = function (obj) {
let jsonVal = JSON.stringify(obj, null, '\t');
let lines = [];
let lineStr = "";
console.log(jsonVal);
for (let i=0;i<jsonVal.length; i++) {
if ('\t' == jsonVal.charAt(i) || '\n' == jsonVal.charAt(i)) {
let s = lineStr.trim();
if (s.length > 0) {
lines.push(lineStr.trim());
}
lineStr = "";
} else {
lineStr += jsonVal.charAt(i);
}
}
lines.push(lineStr);
return lines;
},
this.difference = function (leftStr, rightStr) {
let count = 0, tmpCount = 0;
for (let i=0;i<leftStr.length && i < rightStr.length;i++) {
if (leftStr.charAt(i) == rightStr.charAt(i)) {
tmpCount++;
} else {
count += tmpCount;
tmpCount = 0;
}
}
if (tmpCount > 0) {
count+=tmpCount;
}
return count / Math.max(leftStr.length, rightStr.length) * 100;
},
this.compareFind = function (index, start, left, right) {
let val = {
right: start,
series: 0,
find: false
};
if (index < left.length && start < right.length) {
let score = this.difference(left[index], right[start]);
if (score > 60) {
let rtn = this.compareFind(index + 1, start + 1, left ,right);
val.find = true;
val.series += rtn.series + 1;
}
}
return val;
},
this.compareNext = function (s1, s2, left, right, result) {
let diff = {
left: 0,
series: 0,
right: 0,
find: false
};
for (let i=s1; i - s1 < this.findLen && i< left.length; i++) {
let len = this.compareFind(i, s2, left, right);
if (diff.series < len.series) {
diff.left = i;
diff.series = len.series;
diff.right = len.right;
}
}
if (diff.series > 0) {
for (let i=s1;i<diff.left;i++) {
result[i] = "<span style='background-color: pink'>" + left[i] + "</span>"; // 标记新增的部分
}
let r = diff.right;
for (let i=diff.left; i<diff.left + diff.series;i++) {
let score = this.difference(left[i], right[r++]);
if (score < 100) {
result[i] = "<span style='background-color: yellow'>" + left[i] + "</span>"; // 标记修改的部分
} else {
result[i] = left[i]; // 相同的部分
}
}
diff.find = true;
} else {
// result.push("<span style='background-color: pink'>" + left[s1] + "</span>"); // 标记新增的部分
if (s2 + 1 < right.length) {
let f = this.compareNext(s1, s2 + 1, left, right, result);
return {
series: f.series,
left: f.left,
right: f.right,
find: f.find
}
} else {
diff.left = s1 + 1;
diff.right = s2;
}
}
return {
series: diff.series,
left: diff.left + diff.series,
right: diff.right + diff.series,
find: diff.find
};
},
this.compareJson = function (left, right) {
let result = [];
let liftLine = this.json2Lines(left);
let rightLine = this.json2Lines(right);
let lIndex = 0, rIndex = 0;
while (lIndex < liftLine.length) {
let diff = this.compareNext(lIndex, rIndex, liftLine, rightLine, result);
if (!diff.find) {
result[lIndex] = "<span style='background-color: pink'>" + left[lIndex] + "</span>"; // 标记新增的部分
}
lIndex = diff.left;
rIndex = diff.right;
// break;
}
return result;
},
this.jsonToCompare = function (left, right) {
try {
//json对象排序
let l = this.jsonSort(JSON.parse(left));
let r = this.jsonSort(JSON.parse(right));
let res = this.compareJson(l, r);
return res.join(" ");
} catch (e) {}
return left;
}
}
//调用例子
let json1= {
v1: "abc",
v2: "ccc",
v3: "ddd",
v4: "555"
}, json2= {
v1: "abc",
v2: "ccc",
v3: "eee"
}
let cp = new compare();
cp.jsonToCompare(json1, json2);