利用常规算法带你一步一步解决前端实际业务(附彩蛋)

1,426 阅读8分钟

先简单聊聊

一提到算法,一些小伙伴们听着“打脑壳”,一些小伙伴会觉得天天搞什么排序,二叉树迭代...有啥意思,工作中用不到。哈哈,“老子”说的好,不是用不到,而是你还没到那个level。今天主要是聊聊学习简单的算法在实际工作中的利用,没有什么太高深的东西(白嫖怪请走开),只是跟大家分享我当初的思考过程,从最low的代码一步一步思考,最后到基本还看的过去代码。文章较长!小妹儿,先上鸳鸯锅吃起!

业务场景

添加商品 这个简常见务场景我相信很多小伙伴们都遇到过,大家都知道(SPU)加上一些属性才组成(SKU),比如最常见的“门”(彩蛋--其实我是卖门的小二,会有些小伙伴没有这个生活经验,哈哈!正好给大家普及一下,毕竟将来都是要买房子装修的人,如果小伙伴正好遇到选门这方面的问题留言咨询哈!),门是SPU,门包含了颜色、材质、开口方向等一堆属性,直接上代码!

   
       let directions = ["左锁内开", "左锁外开","右边内开","右锁外开"] //后面给大家配图
       
       let colors = ["象牙白", "胡桃木","苹果木"]
       
       let materials = ["原木", "实木","钛合金"]

那么一个门SKU就应该是["左锁内开","象牙白","实木"]、["右边内开","胡桃木","实木"]等等...大白话就是需要把上面的属性挨个组合起来。下面正式开始代码(每个后台返回的数据解构不一样,实际代码会有差异)

1. 嵌套循环

这种方式是最容易想到了,就是第一个属性数组迭代,里面再放入第二个属性数组,再放第三个...

         let skus = []
         
         for (let i = 0; i < directions.length; i++) {
             
             for (let j = 0; j < colors.length; j++) {

                 for (var k = 0; k < materials.length; k++) {

                     let sku = [];

                     sku.push(directions[i],colors[j],materials[k]);

                     skus.push(sku)

                  } 
              
             }

         } 
   

我的天啦!O(N^3)的复杂度。哈哈,机智的你是不是早就看出了!难道我的属性只有3个?不可能啊,比如你选了钛合金门,还有70料,83料,85料,90料等等,不同的料价格不同也就是不同的SKU啊。所以这个操作肯定是不行的。

2. 两两组合

上面的操作当额外增加属性的时候就不行了,那么我们先解决这个问题。我们采用属性两两组合,也就是先让directions和colors组合成二维数组返回,再和materials组合。上代码!


    let props = [directions, colors, materials,returns],skus = props[0];
      
    for (var k = 1; k < props.length; k++) {
        skus = matching(skus, props[k]);
    }
    function matching(arr, arr1) {
        let sku = []
        if (!!arr.length && !!arr1.length) {
            for (var i = 0; i < arr.length; i++) {
                for (var j = 0; j < arr1.length; j++) {
                     sku.push(`${arr[i]}-${arr1[j]}`)
                }
            }
            return sku
        }
    }

结果如下: 再遍历一次分开就搞定。这种操作从时间复杂度可以看成O(N^2),似乎解决了未知属性集合数量的拼装,但是始终不够优雅。我们继续往下思考!

2. 排列组合(递归回溯)

本来我打算自己贴代码,但是前几天看到一篇博文写的很好,这里大家可以看一下 @晨曦时梦见兮 老师的一篇博文,他写的已经非常仔细了。https://juejin.im/post/6844904191379374087。 如果晨曦的代码看着晕可以看下面每一行加注释的代码。

这里我把思路再整理一下:最终我们需要拼装成二维数组[["左锁内开","象牙白","实木"]、["右边内开","胡桃木","实木"]...],那么我们的递归体就是去完成二维数组里面的每一项的拼装。 为了方便大家读我把每一行代码加了注释。大家在看代码的时候,不要硬看,要学会使用debug,同时也要有良好的js基础,高阶函数、闭包、递归等等。

        function createGoods(...goodsProps){
              // goodsProps 为 已经选择好的属性数组
              let results = [] //输出的二维数组
              function helper(goodsPropsIndex,prevArr){ //定义递归处理函数
                   //拿到当前迭代的哪一个属性数组,默认传递的0,拿第一个属性数组
                   let props = goodsProps[goodsPropsIndex] 
                   //获取是不是最后一组属性,递归的结束条件。
                   let isLast =  goodsPropsIndex == goodsProps.length - 1
                   for(let prop of props){ //遍历其中某一个属性数组
                       //把当前迭代的属性数组中取一个与上次传入的合并。 
                       let cur = prevArr.concat(prop) 
                       //判断是不是最后一个迭代的属性数组
                       if (isLast) {
                       //如果是,就把合并好的一次放入输出数组中
                          results.push(cur)
                       }else{
                       //如果不是,就继续递归下一个属性数组
                          helper(goodsPropsIndex+1,cur) 
                       }
                   } 
              }
              helper(0,[]) //参数一为属性数组的索引,[]为拼装当前这次合并数组
              return results
        }

总结

大家都是从小白慢慢的学习到能独挡一面的,只有非常厉害的人才能一下想到 递归回溯,借用 晨曦老师的话:我们可以先记住一些固定的模式,再从实际工作中去慢慢领会,同时夯实JS基础,多去尝试,萌新终究会变成别人眼中的大佬!再次感谢 @晨曦时梦见兮 老师的博文,帮我省了很多篇幅。

附录

现在主流的家装门是实木贴面,也就是使用实木条加上门皮拼装的,实木条的多少决定了门的质量和强度,千万不要相信销售给你看的小样,在挑选的要从门的中间网上一掌的位置,从左到右敲,如果声音基本一致,那就比较厚道的。同时西南地区市场价包安装大概在1400左右,太低的标注为实木门的就不要买了。