除了 JSON 序列化比较,还有哪些方法可以进行深比较?

简介: 【10月更文挑战第29天】

使用lodash或underscore.js库的深度比较函数

  • lodash的 _.isEqual 方法

    • 原理:lodash是一个广泛使用的JavaScript实用工具库,其 _.isEqual 方法通过递归遍历对象和数组的所有属性和元素,对不同的数据类型进行细致的比较,能够处理各种复杂的数据结构,包括循环引用等特殊情况。
    • 示例

      const _ = require('lodash');
      
      let obj1 = {
              name: 'Alice', age: 25, hobbies: ['reading', 'swimming'] };
      let obj2 = {
              name: 'Alice', age: 25, hobbies: ['reading', 'swimming'] };
      
      console.log(_.isEqual(obj1, obj2)); // true
      
      let obj3 = {
              name: 'Alice', age: 25, details: {
              city: 'New York' } };
      let obj4 = {
              name: 'Alice', age: 25, details: {
              city: 'New York' } };
      
      console.log(_.isEqual(obj3, obj4)); // true
      
  • underscore.js的 _.isEqual 方法

    • 原理:underscore.js同样提供了 _.isEqual 方法来进行深度比较,其实现原理与lodash类似,通过递归和类型判断等方式对对象和数组进行全面的比较,以确定它们是否在结构和值上完全相等。
    • 示例

      const _ = require('underscore');
      
      let obj1 = {
              name: 'Alice', age: 25, hobbies: ['reading', 'swimming'] };
      let obj2 = {
              name: 'Alice', age: 25, hobbies: ['reading', 'swimming'] };
      
      console.log(_.isEqual(obj1, obj2)); // true
      
      let obj3 = {
              name: 'Alice', age: 25, details: {
              city: 'New York' } };
      let obj4 = {
              name: 'Alice', age: 25, details: {
              city: 'New York' } };
      
      console.log(_.isEqual(obj3, obj4)); // true
      

基于对象属性遍历和类型判断的自定义比较函数

  • 原理:这种方法需要开发者手动编写比较逻辑,通过遍历对象的属性,针对不同的数据类型进行相应的比较操作。对于对象类型的属性,继续递归调用比较函数进行深度比较;对于数组类型的属性,遍历数组元素并逐个比较。
  • 示例
function customDeepEqual(obj1, obj2) {
   
  // 比较对象的类型是否相同
  if (Object.prototype.toString.call(obj1)!== Object.prototype.toString.call(obj2)) {
   
    return false;
  }

  if (typeof obj1 === 'object' && obj1!== null) {
   
    const keys1 = Object.keys(obj1);
    const keys2 = Object.keys(obj2);

    if (keys1.length!== keys2.length) {
   
      return false;
    }

    for (let key of keys1) {
   
      if (!obj2.hasOwnProperty(key)) {
   
        return false;
      }

      if (typeof obj1[key] === 'object' && obj1[key]!== null) {
   
        if (!customDeepEqual(obj1[key], obj2[key])) {
   
          return false;
        }
      } else {
   
        if (obj1[key]!== obj2[key]) {
   
          return false;
        }
      }
    }
  }

  return true;
}

let obj1 = {
    name: 'Alice', age: 25, hobbies: ['reading', 'swimming'] };
let obj2 = {
    name: 'Alice', age: 25, hobbies: ['reading', 'swimming'] };

console.log(customDeepEqual(obj1, obj2)); // true

let obj3 = {
    name: 'Alice', age: 25, details: {
    city: 'New York' } };
let obj4 = {
    name: 'Alice', age: 25, details: {
    city: 'New York' } };

console.log(customDeepEqual(obj3, obj4)); // true

使用Proxy对象进行代理比较

  • 原理:利用ES6的Proxy对象可以拦截对象的操作,通过定义 getset 等拦截器,在访问和修改对象属性时进行额外的处理。在深度比较中,可以使用Proxy来代理对象,在属性访问时记录属性值,并递归比较属性值是否相等。
  • 示例
function deepEqualWithProxy(obj1, obj2) {
   
  const seen = new WeakSet();

  const proxyHandler = {
   
    get(target, property) {
   
      if (typeof target[property] === 'object' && target[property]!== null) {
   
        if (!seen.has(target[property])) {
   
          seen.add(target[property]);
          return new Proxy(target[property], proxyHandler);
        }
      }
      return target[property];
    }
  };

  const proxiedObj1 = new Proxy(obj1, proxyHandler);
  const proxiedObj2 = new Proxy(obj2, proxyHandler);

  return JSON.stringify(proxiedObj1) === JSON.stringify(proxiedObj2);
}

let obj1 = {
    name: 'Alice', age: 25, hobbies: ['reading', 'swimming'] };
let obj2 = {
    name: 'Alice', age: 25, hobbies: ['reading', 'swimming'] };

console.log(deepEqualWithProxy(obj1, obj2)); // true

let obj3 = {
    name: 'Alice', age: 25, details: {
    city: 'New York' } };
let obj4 = {
    name: 'Alice', age: 25, details: {
    city: 'New York' } };

console.log(deepEqualWithProxy(obj3, obj4)); // true

这种方法结合了Proxy对象的特性和JSON序列化比较的优点,能够处理循环引用等复杂情况,同时又相对简洁直观。

使用第三方深度比较库,如 deep-equal、fast-deep-equal等

  • deep-equal库

    • 原理:该库专门用于深度比较JavaScript对象和数组,它通过递归遍历对象和数组的属性和元素,对各种数据类型进行准确的比较,支持处理循环引用等复杂的数据结构。
    • 示例

      const deepEqual = require('deep-equal');
      
      let obj1 = {
              name: 'Alice', age: 25, hobbies: ['reading', 'swimming'] };
      let obj2 = {
              name: 'Alice', age: 25, hobbies: ['reading', 'swimming'] };
      
      console.log(deepEqual(obj1, obj2)); // true
      
      let obj3 = {
              name: 'Alice', age: 25, details: {
              city: 'New York' } };
      let obj4 = {
              name: 'Alice', age: 25, details: {
              city: 'New York' } };
      
      console.log(deepEqual(obj3, obj4)); // true
      
  • fast-deep-equal库

    • 原理:与其他深度比较库类似,fast-deep-equal通过优化的算法和数据结构,快速而准确地对对象和数组进行深度比较,能够在保证比较准确性的前提下,提高比较的效率。
    • 示例

      const fastDeepEqual = require('fast-deep-equal');
      
      let obj1 = {
              name: 'Alice', age: 25, hobbies: ['reading', 'swimming'] };
      let obj2 = {
              name: 'Alice', age: 25, hobbies: ['reading', 'swimming'] };
      
      console.log(fastDeepEqual(obj1, obj2)); // true
      
      let obj3 = {
              name: 'Alice', age: 25, details: {
              city: 'New York' } };
      let obj4 = {
              name: 'Alice', age: 25, details: {
              city: 'New York' } };
      
      console.log(fastDeepEqual(obj3, obj4)); // true
      

这些方法都可以用于进行深比较,各有优缺点。在实际应用中,可以根据具体的项目需求、性能要求、数据结构的复杂程度以及是否愿意引入第三方库等因素,选择合适的深比较方法。

相关文章
|
3月前
|
JSON 中间件 Java
【GoGin】(3)Gin的数据渲染和中间件的使用:数据渲染、返回JSON、浅.JSON()源码、中间件、Next()方法
我们在正常注册中间件时,会打断原有的运行流程,但是你可以在中间件函数内部添加Next()方法,这样可以让原有的运行流程继续执行,当原有的运行流程结束后再回来执行中间件内部的内容。​ c.Writer.WriteHeaderNow()还会写入文本流中。可以看到使用next后,正常执行流程中并没有获得到中间件设置的值。接口还提供了一个可以修改ContentType的方法。判断了传入的状态码是否符合正确的状态码,并返回。在内部封装时,只是标注了不同的render类型。再看一下其他返回的类型;
221 3
|
3月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
244 1
|
3月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
260 1
|
4月前
|
XML JSON 编解码
从JSON到Protobuf,深入序列化方案的选型与原理
序列化是数据跨边界传输的“翻译官”,将结构化数据转为二进制流。JSON可读性强但冗余大,Protobuf高效紧凑、性能优越,成主流选择。不同场景需权衡标准化与定制优化,选最合适方案。
390 3
|
6月前
|
JSON 人工智能 Go
在Golang中序列化JSON字符串的教程
在Golang中,使用`json.Marshal()`可将数据结构序列化为JSON格式。若直接对JSON字符串进行序列化,会因转义字符导致错误。解决方案包括使用`[]byte`或`json.RawMessage()`来避免双引号被转义,从而正确实现JSON的序列化与反序列化。
344 7
|
7月前
|
XML JSON Java
go语言之JSON序列化
本文介绍了Go语言中的JSON序列化与反序列化,其操作与Java类似。需要注意的是,由于Go语言的包管理机制,变量和引入包的首字母需大写,以便其他包引用。示例代码展示了如何将`Student`结构体进行JSON序列化(返回字节数组,需转为字符串)及反序列化。此外,文章还说明了通过tag(如`json`和`xml`)指定序列化变量的重要性,以避免因包间访问限制导致反序列化失败或值为null的问题。
151 0
|
8月前
|
JSON JavaScript 前端开发
Go语言JSON 序列化与反序列化 -《Go语言实战指南》
本文介绍了 Go 语言中使用 `encoding/json` 包实现 JSON 与数据结构之间的转换。内容涵盖序列化(`Marshal`)和反序列化(`Unmarshal`),包括基本示例、结构体字段标签的使用、控制字段行为的标签(如 `omitempty` 和 `-`)、处理 `map` 和切片、嵌套结构体序列化、反序列化未知结构(使用 `map[string]interface{}`)以及 JSON 数组的解析。最后通过表格总结了序列化与反序列化的方法及类型要求,帮助开发者快速掌握 JSON 数据处理技巧。
|
JSON 人工智能 算法
探索大型语言模型LLM推理全阶段的JSON格式输出限制方法
本篇文章详细讨论了如何确保大型语言模型(LLMs)输出结构化的JSON格式,这对于提高数据处理的自动化程度和系统的互操作性至关重要。
1895 48
|
JSON JavaScript 前端开发
Go语言中json序列化的一个小坑,建议多留意一下
在Go语言开发中,JSON因其简洁和广泛的兼容性而常用于数据交换,但其在处理数字类型时存在精度问题。本文探讨了JSON序列化的一些局限性,并介绍了两种替代方案:Go特有的gob二进制协议,以及msgpack,两者都能有效解决类型保持和性能优化的问题。
450 7
|
JSON 前端开发 JavaScript
聊聊 Go 语言中的 JSON 序列化与 js 前端交互类型失真问题
在Web开发中,后端与前端的数据交换常使用JSON格式,但JavaScript的数字类型仅能安全处理-2^53到2^53间的整数,超出此范围会导致精度丢失。本文通过Go语言的`encoding/json`包,介绍如何通过将大整数以字符串形式序列化和反序列化,有效解决这一问题,确保前后端数据交换的准确性。
389 4