笔试题-数组去重 | 刷题打卡

简介: 这是一个比较常规的面试手写题,能够考查面试者对各种数据类型特性了解掌握程度与网上所叙述的常规N种方法实现去重又有些不一样本文中咱们先理清思路,然后顺着思路使用朴素的代码进行实现,且满足一切特殊要求

题目描述


实现一个duplicate方法,实现数组的去重


特殊要求


  1. 考虑值类型与引用类型
  1. 引用类型只考虑普通ArrayObject
  2. 可以不考虑Symbol与BigInt
  1. 不考虑function
  2. {}{},{a:1,b:2}{b:2,a:1},[][]算相等
  1. 即只要对象的每一项值都相等那么也算相等
  1. 不能使用set (直接使用set就降低了题目难度)


function duplicate(arr){
    // ...code
}


题目分析


  1. 首先需要考虑的值类型有5种
  • null
  • undefined
  • string
  • boolean
  • number
  1. number 的话需要特殊考虑NaN
  • NaN !== NaN --> true
  1. 判断步骤可以先判断类型是否一致,如果一致再判断内容
  2. 题目特殊要求判断引用类型是否相等的步骤
  1. 有朋友可能会说直接JSON.stringify(obj),说明没有考虑周全,对象的键顺序不一致,导致stringify的结果不一致的问题


解题思路


  1. (笔者) 判断两个引用类型是否值完全一致 的思路
  1. 判断引用类型
  1. 数组
  2. 对象
  1. 数组
  1. 判断数组长度是否一致
  2. 逐项进行比较
  1. 对象
  1. 判断目标对象自身属性键的数量是否一致,一致再判断A是否完全包含B的键
  2. 逐项判断值是否一致
  1. 去重步骤思路
  1. 原数组记为arr,新数组记为res
  2. 遍历arrres的每一项进行比较
  1. 先判断类型是否一致
  2. 都是值类型,则 === 比较
  3. 都是数组,则比较数组的每一项是否一致
  4. 都是对象,则比较是否拥有相同的键且键的值是一样


分步骤实现


功能函数实现


  1. 判断类型是否一致


function isSameType(a, b) {
    // 两者都是值类型
    if (typeof a === typeof b && !(a instanceof Object) && !(b instanceof Object)) {
        return true
    }
    // 两者都是对象
    if (a instanceof Object && b instanceof Object) {
        let aOk = a instanceof Array
        let bOk = b instanceof Array
        // 都是数组,或者都不是数组则ok --> aOK === bOk
        return aOk === bOk
    }
    return false
}


  1. 判断是否是值/引用类型


因为题目的引用类型只包含Array与Object,所以这里简单使用instanceof判断


function isObject(a){
    return a instanceof Object
}
function isValueType(a){
    return !isObject(a)
}


  1. 判断值类型的两者是否值一致


function isSame(a, b) {
    // 为什么不用isNaN
    // 因为isNaN(undefined) 为true
    return (a === b) || (a !== a && b !== b)
}


  1. 判断引用类型的两者是否一致


代码未做优化,可能有点💩💩(完全按照上述思路编写),有优化空间


function isEqual(a, b) {
    if (!isSameType(a, b)) {
        return false
    }
    // 都是数组
    if (Array.isArray(a)) {
        if (a.length !== b.length) {
            return false
        }
        // 逐项判断
        for (let i = 0; i < a.length; i++) {
            let _a = a[i]
            let _b = b[i]
            // 类型不等
            if (!isSameType(_a, _b)) {
                return false
            }
            // 值类型,值不等
            if (isValueType(_a) && !isSame(_a, _b)) {
                return false
            }
            // 对象 - 递归判断了
            if(isObject(_a)&&!isEqual(_a,_b)){
                return false
            }
        }
    }
    else {
        // 都是普通对象
        let aKeys = Reflect.ownKeys(a)
        let bKeys = Reflect.ownKeys(b)
        // 键数量不一致
        if(aKeys.length!==bKeys.length){
            return false
        }
        for (const aKey of aKeys) {
            let _a = a[aKey]
            let _b = b[aKey]
            // 类型不等
            if (!isSameType(_a, _b)) {
                return false
            }
            // 值类型,值不等
            if (isValueType(_a) && !isSame(_a, _b)) {
                return false
            }
            // 对象 - 递归判断了
            if(isObject(_a)&&!isEqual(_a,_b)){
                return false
            }
        }
    }
    return true
}


准备工作到此算完成了,现在咱们开始组装零件


去重函数实现


function duplicate(arr) {
    // 存放最终结果
    const res = []
    // 遍历咱们的原数组
    for (const a of arr) {
        // 判断是否存在
        let isExist = res.findIndex(b => {
            // 这里就变成了当前项与原来的每一项进行比较
            if (!isSameType(a, b)) {
                return false
            }
            if (isValueType(a) && !isSame(a, b)) {
                return false
            }
            if (isObject(a) && !isEqual(a, b)) {
                return false
            }
            return true
        }) !== -1
        // 不存在则放入
        if (!isExist) {
            res.push(a)
        }
    }
    return res
}


最终测试


测试数据


let obj1 = { age: [1, 2, { data: [1, 2, 3], name: 'a' }], name: 'xm' }
let obj2 = { name: 'xm', age: [1, 2, { name: 'a', data: [1, 2, 3] }] }
let a = [1, 2, '1', '2', 1, null, null, undefined, undefined, {}, {}, [], [], [1], [1], [1, 2], [2, 1], ['1'], ['1'], obj1, obj2, false, false, NaN,NaN, true, true]


结果


duplicate(a)
[
  1,
  2,
  '1',
  '2',
  null,
  undefined,
  {},
  [],
  [ 1 ],
  [ 1, 2 ],
  [ 2, 1 ],
  [ '1' ],
  { age: [ 1, 2, { data: [1, 2, 3], name: 'a' } ], name: 'xm' },
  false,
  NaN,
  true
]


总结


  1. 考查知识点
  1. 类型判断
  2. 值类型特点
  3. 引用类型特点
  4. NaN的特点
  1. 整个从代码量看下来,就是判断两个对象是否值相等那里稍微复杂一点
  2. 做笔试题只要把边界情况考虑清楚,理清思路,在不考虑时间复杂度的时候,用朴素的代码就能完成


相关文章
|
前端开发 JavaScript NoSQL
从前端到后端:构建现代化的全栈应用
本文将探讨如何构建现代化的全栈应用,从前端到后端的技术选型、架构设计和开发实践等方面进行详细介绍。我们将深入研究各种技术工具和框架,如前端开发中的React和Vue,后端开发中的Java和Python,以及数据库管理与优化等,帮助读者全面了解全栈开发的核心概念和实际应用。
|
JavaScript 前端开发 Java
JavaScript中的面向对象编程(OOP) - 终极指南
本文介绍了 JavaScript 的面向对象编程 (OOP) 概念,包括继承、多态、封装和抽象等关键要素,并通过代码示例帮助开发者理解和应用 OOP 思维。
239 5
|
人工智能 JavaScript 前端开发
利用 AI 进行代码生成:GitHub Copilot 的实践与反思
【10月更文挑战第23天】本文探讨了GitHub Copilot,一个由微软和OpenAI合作推出的AI代码生成工具,其核心功能包括智能代码补全、多语言支持、上下文感知和持续学习。文章介绍了Copilot在加速开发流程、学习新语言、提高代码质量和减少重复工作等方面的应用,并反思了AI在代码生成中的代码所有权、安全性和技能发展等问题。最后,文章提供了实施Copilot的最佳实践,强调了在使用AI工具时保持对代码的控制和理解的重要性。
|
人工智能 vr&ar Android开发
探索Android应用开发的未来趋势
随着技术的不断进步,移动应用开发领域正迎来前所未有的变化。本文将深入探讨Android平台在新技术推动下的发展动向,包括人工智能、物联网(IoT)、5G网络以及增强现实(AR)和虚拟现实(VR)技术如何塑造未来的移动应用。我们将分析这些技术如何影响用户体验、数据安全和隐私保护,以及开发者如何适应这些变化以保持竞争力。
|
机器学习/深度学习 算法 数据挖掘
R语言中的支持向量机(SVM)与K最近邻(KNN)算法实现与应用
【9月更文挑战第2天】无论是支持向量机还是K最近邻算法,都是机器学习中非常重要的分类算法。它们在R语言中的实现相对简单,但各有其优缺点和适用场景。在实际应用中,应根据数据的特性、任务的需求以及计算资源的限制来选择合适的算法。通过不断地实践和探索,我们可以更好地掌握这些算法并应用到实际的数据分析和机器学习任务中。
|
消息中间件 存储 NoSQL
[Kafka 常见面试题]如何保证消息的不重复不丢失
[Kafka 常见面试题]如何保证消息的不重复不丢失
1245 0
|
iOS开发 开发者
苹果iOS App Store上架操作流程
苹果iOS App Store上架操作流程
|
JavaScript
js更改网页默认右键菜单
js更改网页默认右键菜单
211 0
|
缓存 C语言 数据安全/隐私保护
|
C语言
初识C语言——常量、变量
初识C语言——常量、变量
281 0