利用tire实现带无库存标注的规格选择思路 只是思路没有实现

数据格式

一般数据结构就是这种
抽象出来最基本的数据类型就是一个多维数组

[[30,40,50,60], [“电动车”, “电脑”, “驾照”]]

image.png

没有库存

但麻烦的是有的sku库存为0,比如要实现这种ui样式;
“红色/8G/i5“和“红色/8G/i7“的库存为0,在选择了“红色“和“8G“后“cpu的i5和i7”就要变灰色不能再选了;
选择和“红色”和“i5”后8G也不能再选了。
image.png

笛卡尔积

做笛卡尔积就包含了所有的规格属性值组合的sku;
后台接口一般会把做好笛卡尔积的所有sku列表已字典的形式返回;

  1. {
  2. "红色/4G/i3":"库存0",
  3. "红色/4G/i5":"库存99",
  4. ...
  5. }

前端需要根据用户交互选择的前两个规格禁用掉第三个属性的规格,根据前一个禁用后两个,甚至是全部禁用。
一年多前写过一个带库存的规格选择,现在回头看完全不知道写了写什么。
https://blog.csdn.net/weixin_42519137/article/details/89294904
也没有参考意义了

利用前缀tire

前些阵子用tire做了一个站内导航
https://www.yuque.com/wuzhao/kb/wgo04g
突发奇想或许可以用这个前缀tire实现没库存的规格选择

思路

1、笛卡尔积,生成所有的sku
2、每一个sku的属性全排列插入字典树
防止出现”4G/红色”在前缀树中无法匹配的问题
全排列代码参考

https://leetcode-cn.com/problems/permutations/

3、如果当前选择规格属性值在前缀树中匹配了库存为0的sku,则把这个sku属性中没有选择的值在视图上标灰,不能再选择。

具体实现我暂时就不做了

具体实现:这个仓库,现在我不做,以后有时间可能就做了。
https://github.com/withwz/tire-spec-select


笛卡尔积代码

 function cartesian(arr) {
      if (arr.length < 2) return arr[0] || [];
      return [].reduce.call(arr, function (col, set) {
        let res = [];
        col.forEach(c => {
          set.forEach(s => {
            let t = [].concat(Array.isArray(c) ? c : [c]);
            t.push(s);
            res.push(t);
          })
        });
        return res;
      });
    }

    const res = cartesian([[30,40,50,60], ["电动车", "电脑", "驾照"]])
    console.log(res)

前缀树代码

class TrieNode {
  constructor() {
    this.numPass = 0;//有多少个单词经过这节点
    this.numEnd = 0; //统计单词的个数
    this.edges = [];
    this.value = ""; //value为单个字符
    this.isEnd = false;
  }
}

/**
 * 前缀树
 * -----
 * @see https://segmentfault.com/a/1190000013018855
 */
class Trie {
  constructor() {
    this.root = new TrieNode();
  }


  insert(word) {
    var cur = this.root;
    for (var i = 0; i < word.length; i++) {
      var c = word.charCodeAt(i);
      c -= 48; //减少“0”的charCode
      var node = cur.edges[c];
      if (node == null) {
        var node = (cur.edges[c] = new TrieNode());
        node.value = word.charAt(i);
        node.numPass = 1; //有N个字符串经过它
      } else {
        node.numPass++;
      }
      cur = node;
    }
    cur.isEnd = true; //樯记有字符串到此节点已经结束
    cur.numEnd++; //这个字符串重复次数
    return true;
  }

  /**
   * 先序遍历
   * @param {function} cb 
   */
  preTraversal(cb) {
    function preTraversalImpl(root, str, cb) {
      cb(root, str);
      for (let i = 0, n = root.edges.length; i < n; i++) {
        let node = root.edges[i];
        if (node) {
          preTraversalImpl(node, str + node.value, cb);
        }
      }
    }
    preTraversalImpl(this.root, "", cb);
  }

}

测试

var trie = new Trie();
trie.insert("I");
trie.insert("Love");
trie.insert("武昭");
trie.insert("武昭");
trie.insert("武昭在哪里???");
trie.insert("武昭在干什么???");
trie.insert("武昭🙂???");
trie.insert("武昭没吃饭???");
trie.insert("China");
trie.insert("China");
trie.insert("China");
trie.insert("China");
trie.insert("xiaoliang");
trie.insert("xiaoliang");
trie.insert("man");
trie.insert("handsome");
trie.insert("love");
trie.insert("Chinaha");
trie.insert("her");
trie.insert("know");

console.log("包含武(包括本身)前缀的单词及出现次数:");
console.log("---------------start--------------------")
var map = {}
trie.preTraversal(function (node, str) {
  if (str.indexOf("武") === 0 && node.isEnd) {
    map[str] = node.numEnd
  }
})
for (var i in map) {
  console.log(i + " 出现了" + map[i] + " 次")
}

image.png