[工作中遇到的难点] 数组去重算法高级应用

今天在做需求的时候遇到了一个问题。有一堆产品对象放在一个数组arrs里,每个对象都有imageUrl,brand(产品品牌),name(产品名称),dimension(产品规格),quantity(产品数量,可能是面积和个数),id,type(根据位置分类),unit(单位),conpouType(产品套餐类型)等属性;
要求把imageUrl,brand,name,dimension等属性相同的产品给合并并显示到界面上,但是在判断两个产品是否相等的时候,不能把有些属性比如quantity,type进行比较。
展示到界面上的产品如图:


产品展示.png

其中unit可能是 ‘个’,‘面积’,当unit是个的时候quantity只可能是1。

刚拿到这个需求,想到javascript经典的去重算法。就开始用算法复杂度最低的去重算法思路来实现这个需求。

大致思路:

1.根据产品id,name,先排序;
2.把arrs中的产品对象排完序后,for循环数组arrs,每次循环都要判断相邻两个对象是否深度相等(有的属性可能也是对象)。如果相等,就把两个对象的quantity相加,否则就不做处理。

咋一看,好像思路挺简单的,实现起来应该也没什么问题。
可是我忽略了我们程序中复杂多变的情况。

坑1:

有的产品没有产品id:测试去测的时候发现,总是出现相同,最后发现有的产品竟然没有id,经过考虑,个人觉得还是通过id判断比较合适,可是又没有其它字段可以代表没有id的产品。后来发现可以name在这个问题上有一定的决定权,所以就先用id来判断,如果没有id,再用name来判断。

坑2:

先用id再用name,看起来简单,在实现的时候还是犯了致命的错误。
如下代码,咋一看,好像没问题,实际上,问题大了

        contents.sort((a, b) => {
          if(a.Id) {
            if(a.Id > b.Id) return 1;
            if(a.Id< b.Id) return -1;
            if(a.Id=== b.Id) return 0;            
          } else {
            if(a.name > b.name) return 1;
            if(a.name < b.name) return -1;
            if(a.name === b.name) return 0;
          }
        });

在发布前夕,又遇到相同类型的没有合并的问题,这次更神奇,生产上是错的,测试环境是对的。
看来是数据的问题,调了下代码,发现生产和测试环境的数据顺序不一样,仔细看了下上述排序过程,发现少判断了b.Id是不是存在的。如果a.Id存在,b.Id不存在,并且和b.Id不存在的这个产品相同的其它产品,都是用name排的序,它自己用Id排的序,不出问题才怪。

坑3:

上述算法===写成了=

坑4:

属性值有错,同一个属性的值,有的是[],有的竟然是undefined,于是找了数据来源方,控制数据类型的一致性。

坑5:

没有把type类型,剔除出去,结果在比较对象是否相等的时候,相同name的有好多type是不相等的。
于是我想了一馊主意。先根据产品Id或者name排序,排完序后,再根据产品type排序。当时的我一定没睡醒。

       contents.sort((a, b) => {
          if(a.Id && b.Id) {
            if(a.Id > b.Id) return 1;
            if(a.Id< b.Id) return -1;
            if(a.Id=== b.Id) return 0;            
          } else {
            if(a.name > b.name) return 1;
            if(a.name < b.name) return -1;
            if(a.name === b.name) return 0;
          }
        });

       contents.sort((a, b) => {
          if(a.type) {
            if(a.type > b.type) return 1;
            if(a.type < b.type) return -1;
            if(a.type === b.type) return 0;            
          }
        });

毋庸置疑,这次测试又报bug了。
调了下代码,发现有两个问题,一个问题是有的产品name可能是空字符串,倘若如此,那不如不显示这些产品。于是去说服了测试,产品。
另一个问题是本来排的好好的序,name相同的为一组,再用type来排序,把name相同,type不同的类型活生生地拆散。然而,type并不是是否要合并产品的关键字段。所以这才把type从之前的比较对象的数组中剔除了,管它相不相等,按照name不为空的逻辑,正常排序就好。在后面合并的过程中,type不等,name和其它有必要比较的属性都相等相同就可以合并了。

总结:

这些bug集中在排序算法上,也怪我平时对这个算法理解的不够深入,总是想当然。倒是排序后的去重算法,没有出现过多的bug,看来平时刷的算法题还是有用的,关键时刻能减少bug。
针对该算法,写了一个类似的算法,放到了github上。
水果统计个数

带着对自己的谴责和对公司代码的无奈写完了这篇文章。只能说,革命尚未成功,同志仍需努力。

---------------------更新------------------
后来这个bug又继续重现,一直有相关产品没能合并。
原因:
在比较Id的时候,发现没有Id,但在比较name的时候name相同,但是其它字段,比如brand并不相同。
这把我先排序再去重的思路完全打乱了,我干嘛要排序呢,既然没有一个指定的字段可以用来排序的,排序完全就是多此一举。
但是如果用这种思路继续写的话,怎么写才对呢?
我把每个字段都做了一个比较,得到如下代码:

        inValidContents.sort((a, b) => {
          if ((a.Id && b.Id) && a.Id !== b.Id) {
              if(a.Id > b.Id) return 1;
              if(a.Id < b.Id) return -1;
          } else if ((a.name && b.name) && a.name !== b.name) {
              if(a.name > b.name) return 1;
              if(a.name < b.name) return -1;
          } else if((a.brand && b.brand) && a.brand !== b.brand) {
              if(a.brand > b.brand) return 1;
              if(a.brand < b.brand) return -1;
          } else if ((a.imageUrl && b.imageUrl) && a.imageUrl !== b.imageUrl) {
              if(a.imageUrl > b.imageUrl) return 1;
              if(a.imageUrl < b.imageUrl) return -1;
          } else if (!isEqualArray(a.dimension, b.dimension)) {
              if(getArrayStr(a.dimension) > getArrayStr(b.dimension)) return 1;
              if(getArrayStr(a.dimension) < getArrayStr(b.dimension)) return -1;
          } else if(!isEqualArray(a.couponTypes, b.couponTypes)) {
              if(getArrayStr(a.couponTypes) > getArrayStr(b.couponTypes)) return 1;
              if(getArrayStr(a.couponTypes) < getArrayStr(b.couponTypes)) return -1;
          }
          return 0;
        });

上述代码必须要有是否存在的&&判断,因为结合起来我们复杂的数据类型,如果两个Id不相等,一个是undefined,另一个是“”虽然不相等,但等同于相等,所以不会继续进行判断,但是这次比较结果却是无效的。

上述,排序用了至少nlogn的复杂度,再进行循环一个个比较,虽然为n,但是该算法由于不太稳定,对属性的依赖过大。
所以采取新的去重思路,直接就地比较,发现有相同的就去除该数组元素,同时减少该数组长度,直到遍历完全:
代码如下

    //往前移动,发现与当前重复的,就删除
    function removeDuplicate3(arr) {
      let len = arr.length;
      let results = [];
      for(var i = 0;i < len;i++) {
        for(var j = i + 1;j < len;j++) {
          if(arr[i] === arr[j]) {
            arr.splice(j,1);
            j--;
            len--;
          }
        }
      }

      return arr;
    }
    var a = [1,2,3,4,5,6,5,3,2,4,56,4,1,2,1,1,1,1,1,1,];
    console.log(removeDuplicate3(a));

这篇文章涉及到的去重算法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,622评论 18 399
  • 总结一个月的践行结果。从学习正面管教后反思在对待教育儿子方面的可取与不足之后,根据老师的指导开始打卡,通过这种方式...
    嘟嘟小妈阅读 409评论 0 0
  • 这几天天天关注12306,因为要帮小姑和妈妈买票。唯独的一趟动车,一点半开售,时间到,开始刷一直显示13点30分开...
    心灵深处ye阅读 239评论 0 0
  • 1月15日 星期一 深圳晴 今天忙活了一天了,焦头烂额的,哈哈。不过状态还是很不错的,忙起来才会觉...
    能量女王刘大红阅读 300评论 0 0