Recently, I met a requirement to make a commodity detail page similar to that of jd.com or Taobao, among which there is a function of selecting and inquiring the SKU of commodities

As shown in the figure above, network type, body color, package type and storage capacity are all SKU attributes. After selecting all SKU attributes, they will be combined into a complete SKU corresponding to a specific product, and then the inventory and price information of the corresponding product of this SKU can be given

In addition, when certain SKU attributes are selected, it will automatically calculate the SKU combination with the inventory of 0 under the current condition according to the SKU attributes that have been selected, giving the button an interaction that is not optional

The start when receiving the demand, the demand of the backend and the front end of the group cooperation friends all think this is a difficult point, need a good research, and I was evil spirit’s smile, don’t think it is a combination, algorithm can write write, can’t write a big deal for a loop through violence, little scene

However, when I started working on it, I realized that, unlike usual, the code couldn’t be written with my eyes closed

Implementation approach

When I found this function was a little bit difficult, I started to search for relevant articles on the Internet, which found that the relevant articles seemed to be quite interesting. “You don’t know to what an array of ten kinds of usage of the prototype chain man-of-the-match analysis what my functional programming can’t be so lovely,” what the three years’ experience with a front companies interview all don’t, look incredibly need with the brain, and related articles is less, remove those copy to copy, and I didn’t search, A few articles of real practical value:

  • Realization of SKU combination query algorithm for e-commerce platform
  • Sku multi-dimensional attribute state judgment algorithm
  • Taobao SKU combination query algorithm

After I read it, I was lost in thought

img

Since plastic dinosaurs are made of plastic, and plastic comes from oil, which is converted from ancient dinosaur carcasses. So, plastic dinosaurs are real dinosaurs?

The description of these articles is not very clear, mainly because there is no complete runnable code, can not be verified by myself, full of words to see the clouds in the fog, according to what they said, it is better for me to write a new, but there is some reference significance

  • First, give us a couple of groups
    SKUProperties, then based on the combination of these properties
    skuIt is certainly possible to list all of them, and considering that in real business use, there are not too many attributes to care about extreme cases, so even if all of them are exhausted, the performance will not suffer much;
  • Then, since poor point to the set of all possible, you can calculate the combination of each corresponding price and inventory information, so that nature can form a dictionary, no matter choose what combination, can quickly from the dictionary query to the corresponding information such as price and inventory, i.e., according to the space in time, As long as we initialize this at the beginning with all the possible data dictionaries, and then
    skuProperty switching is nothing more than a dictionary
    keyThe change of the

Of course, that’s the idea, but when you’re actually writing code, you have to think about a lot more points, and you have to think about all of them, and whatever you’re missing, the data is going to be wrong

Data preparation

Assume that for the category of mobile phone, its SKU attributes include finished color, color, configuration and version. The finished color is new and only unwrapped, the color is deep space gray, silver and gold, the configuration is 64G and 256G, and the version is guohong, Hong Kong and Macao, Japan and South Korea, and other versions

Each SKU attribute must have its own unique identifier ID. For example, for a color, it must have its own ID, called paramId. There is at least one small attribute under the color, such as deep space gray, silver and gold.

interface ISkuParamItem {
  paramId: string
  paramValue: string
  valueList: Array<{
    valueId: string
    valueValue: string
  }>
}Copy the code

When skU data is requested, the back end will return all SKU attributes corresponding to the current commodity ID(called spuId), which is called the SKU attribute data set. The data format is tentatively as follows:

[{
    "paramId": "6977"."paramValue": "Colour"."valueList": [{
        "valueId": "1081969"."valueValue": "New"
    }, {
        "valueId": "1080699"."valueValue": "Unseal only."}]}, {"paramId": "6975"."paramValue": "Color"."valueList": [{
        "valueId": "730003"."valueValue": "Deep space Grey"
    }, {
        "valueId": "730004"."valueValue": "Silver"
    }, {
        "valueId": "730005"."valueValue": "Golden"}]}, {"paramId": "7335"."paramValue": "Configuration"."valueList": [{
        "valueId": "710004"."valueValue": "64G"
    }, {
        "valueId": "710006"."valueValue": "256G"}]}, {"paramId": "72"."paramValue": "Version"."valueList": [{
        "valueId": "1080627"."valueValue": "Legal channels"
    }, {
        "valueId": "1080628"."valueValue": "Hong Kong and Macao Edition"
    }, {
        "valueId": "1080697"."valueValue": "Japan and South Korea"
    }, {
        "valueId": "1080629"."valueValue": "Other versions"}}]]Copy the code

All SKU data corresponding to the current commodity ID(called spuId) is returned, which is called the full SKU data set of the commodity. The data format is tentatively as follows:

[
  {
      count: 98,
      paramIdJoin: "72 _1080697__6975_730004__6977_1081969__7335_710006",
      priceRange: [7000, 8978],
      spuDId: "98002993445"}]Copy the code

ParamIdJoin is a combination of SKU attributes and can be divided into four units: 72_1080697, 6975_730004, 6977_1081969, and 7335_710006. Each unit is connected by paramId and valueId. So 72_1080697__6975_730004__6977_1081969__7335_710006 means the SKU combination with Japanese and Korean version, silver color, new color and 256G configuration, corresponding total inventory count is 98. The price range is 7000 to 8978, that is, the lowest price is 7000 and the highest price is 8978. (If there is only one price without high and low prices, the item in the array is the one price.) The SPU id spuDId is 98002993445

Note that the paramIdJoin values, such as 72_1080697__6975_730004__6977_1081969__7335_710006, must be joined in ascending (or descending) order by paramId. 72 < 6975 < 6977 < 7335, so there is 72_1080697__6975_730004__6977_1081969__7335_710006

This plays a significant role in the optimization of the subsequent algorithm, and it is ok not to follow the order, but in the case of a large amount of data, the calculation time will be long, and the page is likely to be stuck

The backend might return data structures and is not the same as above, but has little to do, regardless of the back-end data structure is what kind of return, the data returned certainly need to include those above, because these are the necessary data, be short of one cannot, if not the same, as for the data structure you only need to deal with the first, processing and the same data structure for the above, You can definitely do that and with that data, you can start writing the core processing code

Analysis methods

In fact, there are only two functions, that is to select or cancel any SKU attribute:

  • Calculate the price and inventory of the skU attributes such as color: silver, memory: 64G, carrier: Mobile, the price and inventory of the skU attributes

  • Calculate which SKU combinations need to be grayed under the skU attribute combinations at this time. For example, if the color is currently selected: silver, memory: If the remaining SKU attributes combined with the two selected SKUs have a combined inventory of 0, then these SKU attributes should be grayed out. For example, if the inventory of silver + 64G is 0, then the silver is selected. The 64G SKU property needs to be grayed out

For the first point, relatively simple, just need to get the current selected SKU attribute combination, and then in all SKU data to find the data containing the selected SKU attribute, is a traversal screening operation, difficult is the second point

Regardless of which SKU attributes are currently selected, find the data that contains any of the selected SKU attributes from the entire SKU data, find the data with an inventory of 0 in that data, and find the SKU attributes that should be grayed out from that data

For example, if the inventory of the SKU silver-64G-Bank is 0, then when you select silver-64G, you should gray the SKU; if you select Silver-64G, you should gray the SKU; if you select Silver-64G, you should gray the SKU; if you select China-64G, you should gray the skU

This is just the simplest case

To make things more complicated, assume that the inventory of silver-64G SKU is 0, then when you select silver, you should gray 64G, when you select 64G, you should gray silver, when you select deep space gray-64G, you should gray silver… , etc.

If there are many skU attribute items, such as color, memory, carrier, standard, finish, package type, insurance, then there are many more items that can be combined

Just think about it, it feels so big, it seems like a lot of things to calculate

However, there is evidence to be found

The SKU attributes that should be grayed when any of n SKU attributes are selected are the SKU attributes that should be grayed when any of n SKU attributes are selected, plus the SKU attributes that should be grayed when any two of n SKU attributes are selected, plus… The SKU attribute header that should be grayed with any of these N SKU attributes appears to be larger

img

For example above,

When the skU attributes new, Gold, 64G, and National are selected, calculate the SKU attributes that need to be gray in this state:

First of all, take a look at the four SKU attributes. When no SKU attribute is selected, which SKUS have 0 inventory. It is found that there is no new attribute in all goods, indicating that the new inventory is 0, and record it.

Then, look at the four SKU attributes, each individual SKU should be gray when selected, for example, when the new SKU attribute is individually selected, it is found that the inventory of the combination of new – silver gray is 0, the silver gray will be gray, this attribute is recorded; When the SKU attribute of The Bank of China is selected separately, it is found that the inventory of the combination of the bank of China – unpacking only is 0, then it will only unpack the ash, and this attribute will be recorded. When gold or 64G is selected separately, any combination of other SKU attributes will be stored, and no SKU attributes will be recorded.

Then, look at the four SKU attributes, the skU attributes should be gray when the two SKU attributes are selected. For example, when the two SKU attributes of gold and 64G are selected at the same time, it is found that the combination of gold-64G-port inventory is 0, then the port is gray, and this attribute is recorded. If you select all-new – Gold or all-64G or all-new – gb or gold-GB or 64G-GB at the same time, you will find that any skU combined with these skU has inventory, and no SKU attributes will be recorded.

Then, look at the four SKU attributes, the skU attributes should be gray when the three SKU are selected, for example, when the two SKU attributes of gold and 64G are selected at the same time, it is found that the combination of gold-64G-port inventory is 0, the port will be gray, the attribute is recorded; If you select all-new – Gold or all-64G or all-new – gb or gold-GB or 64G-GB at the same time, you will find that any skU combined with these skU has inventory, and no SKU attributes will be recorded.

img

“Should be grayed” means that for the current SKU combination, if one more SKU attribute is added and the corresponding inventory of the new SKU combination is 0, the last skU attribute should be grayed

So we need a set of SKU attributes that should be grayed out when any number of SKU attributes are selected

Want to get this collection, the computation will be very big, and the vast majority are useless calculations (calculate a lot of results, in fact, the user need only a few), so simplify, you just need to get a collection, the collection contains all may choose sku combination, also can be a bit more orderly, will be set into the Map, In JS this is an object that has properties whose key values are the numbers 1, 2, 3… N-1, n, represents the selection of 1 to N arbitrary SKU attributes, and its value is an object. The key of this small object is a possible combination of 1 to N arbitrary SKU attributes, and its value contains information such as price range and inventory of these combinations

The code level data structure is as follows (partially) :

img

In the case that 1 skU attribute (subscript 0) is selected, there are 11 SKU selection methods, each of which has corresponding price range and total inventory information, such as 72_1080627, when the SKU attribute whose version is the national line is selected, Its price range array priceArr and total inventory totalCount; For the case that 2 skU attributes (subscript 1) are selected, there are 44 SKU options, each of which has the corresponding price range and total inventory information, for example, 72_1080627__6975_730003 represents, when the SKU attribute that version is national line and color is deep space gray is selected, Its price range array priceArr and total inventory totalCount… , etc.

With this information, the rest will be easy

First of all, if you select any SKU attribute, I can find the corresponding total inventory and price range from the set without having to sift through the raw data multiple times, which implements the first function point; For the second function, when any SKU attribute is selected (set as N), I only need to select the data whose key is from 0 to N in this object, and find the data whose total inventory of skU corresponding to these data is 0, and these data contain SKU attribute need to be gray, which realizes the second function point

So, the most critical is the Map object, as long as the calculation of the Map object, the rest of the easy to do, of course, simple to say, really use code or there are a lot of places to pay attention to

img

Data calculation

Calculates the subscript set to which a single SKU attribute belongs

In the data source returned from the interface containing all skU attribute combinations and their corresponding data (i.e., the commodity full SKU data set described above), the set of data items containing the subscripts of each SKU attribute is calculated

For example, for the skU property color: black, all of the items in the skU data set contain colors: The set of subscripts of the data of the skU attribute in black is the subscript set to which it belongs. With paramId and valueId of the SKU attribute as the key and the subscript set as the value, a set can be obtained with the data structure as follows:

{
    "6977 _1081969": [0, 4, 5, 6, 7, 11, 14, 15, 17, 19, 20, 23, 25, 31, 32, 34, 35, 36, 38, 39, 40, 42, 44, 47]"6977 _1080699": [1, 2, 3, 8, 9, 10, 12, 13, 16, 18, 21, 22, 24, 26, 27, 29, 30, 33, 37, 41, 43, 45, 46]"6975 _730003": [4, 14, 17, 18, 22, 23, 28, 29, 32, 33, 37, 39, 43, 44, 45, 47]"6975 _730004": [0, 2, 5, 7, 8, 10, 12, 16, 19, 20, 24, 31, 34, 40, 41 and 46]."6975 _730005": [1, 3, 6, 9, 11, 13, 15, 21, 25, 26, 27, 30, 35, 36, 38, 42]."7335 _710004": [1, 3, 4, 5, 8, 9, 10, 11, 19, 24, 28, 30, 31, 32, 33, 34, 35, 36, 37, 39, 42, 43, 44, 46]"7335 _710006": [0, 2, 6, 7, 12, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23, 25, 26, 27, 29, 38, 40, 41, 45, 47]"72 _1080627": [7, 12, 13, 18, 19, 23, 30, 33, 38, 42, 44, 46)."72 _1080628": [3, 5, 6, 10, 16, 21, 36, 39, 40, 43, 45, 47]"72 _1080697": [0, 8, 9, 11, 14, 22, 25, 26, 31, 32, 37, 41],
    "72 _1080629": [1, 2, 4, 15, 17, 20, 24, 27, 28, 29, 34, 35]}Copy the code

For example, for the first data “6977_1081969”: [0, 4, 5, 6, 7, 11, 14, 15, 17, 19, 20, 23, 25, 31, 32, 34, 35, 36, 38, 39, 40, 42, 44, 47] It represents all skU attributes including paramId 6977 and valueId 1081969 in the commodity full SKU data set, namely, the completion color: The new data subscript set is [0, 4, 5, 6, 7, 11, 14, 15, 17, 19, 20, 23, 25, 31, 32, 34, 35, 36, 38, 39, 40, 42, 44, 47]

It’s just an array of all the skU data sets that we’re going to iterate through, and we’re going to call it keyRankMap, right

Computing a Map object

With keyRankMap above, we have the prerequisites for calculating the desired Map object (called indexKeyInfoMap),

The composition of the indexKeyInfoMap set, as described earlier, is similar to a cache of data that enumerates information for any combination of SKUs, so that involves an algorithm:

How many ways can I take n(n <= m) from m arrays that are not empty? Specify that each array can be fetched at most once, and at most one item at a time

This algorithm is actually a more enhanced version of the common algorithm, that is, from m number of n number, how many kinds of method, the m number here may be regarded as an array, which is from the data of a length of m n number, but now is not from an array, but from the m in the array

So this algorithm is essentially a permutation, and it’s not that hard, but there’s a lot of ways to write it, as long as it works and it’s not too inefficient, right

(composeMArrN(((([1, 2, 3], 2)))); /** * (composeMArrN((([1, 2, 3], 2)))); Example: composeMArrN((([1, 2, 3], 2))) * [[0, 0, 1], [0, 1, 1], [0, 1, 0], [0, 1, 1], [0, 1, 2], [0, 1], [1, 1], [1,0,2], [1, 0], [1,1,1], [1,1,2]] * the returned array length is 11, For example, for the first subarray [0, 0, -1] of the above result, the first method is to take the number with subscript 0 in the first array and the number with subscript 0 in the second array. The subscript 2 entry is -1, which means that the third array does not take any number * @param mArr data source information * @param n Number of numbers * @param arr is used recursion, External calls do not need to pass this item * @param hasSeletedArr recursive use, external calls do not need to pass this item */function composeMArrN (mArr: Array<number>, n: number, arr = [], hasSeletedArr = [], rootArr = []): Array<Array<number>> {
  if(! n || n < 1 || mArr.length < n) {return arr
  }
  for (leti = 0; i < mArr.length; I++) {// the current level already has a check itemif (hasSeletedArr.includes(i)) continue
    hasSeletedArr = hasSeletedArr.slice()
    hasSeletedArr.push(i)
    for (let j = 0; j < mArr[i]; j++) {
      let arr1 = completeArr(arr, i - arr.length, -1)
      arr1.push(j)
      if (n === 1) {
        arr1 = completeArr(arr1, mArr.length - arr1.length, -1)
        rootArr.push(arr1)
      } else {
        composeMArrN(mArr, n - 1, arr1, hasSeletedArr, rootArr)
      }
    }
  }
  return rootArr
}Copy the code

CompleteArr is an auxiliary method for completing an array, for example, for the array [1, 2], I want to increase its length by 3, and fill in the extra position with -1, calling completeArr([1, 2], 3, -1), Return [1, 2, -1, -1, -1], this logic has nothing to do with the algorithm itself, mainly based on the current business considerations, convenient for subsequent data processing

In this way, when n SKU attributes are arbitrarily selected, the corresponding price and inventory information of the SKU combination can be obtained, namely indexKeyInfoMap

Based on indexKeyInfoMap, the corresponding information and grashing information of SKU combinations in any state are calculated

IndexKeyInfoMap is only a cache of data. The indexKeyInfoMap is used to select any SKU data, the corresponding price and inventory information, and the SKU information that should be grayed

For example, for the all-silver combination

  • Calculate the corresponding price and inventory information

Select * from indexKeyInfoMap where key = 1; select * from indexKeyInfoMap where key = 1; select * from indexKeyInfoMap where key = 6975_730004__6977_1081969;

img

Where priceArr represents the set of price ranges for all SKU data containing the new – silver SKU combination, spuDIdArr represents the set of spuDId, and totalCount represents the total inventory

  • The calculation needs to be grayed
    skuattribute

That’s the point

First, calculate the property with zero inventory when none is selected. Second, calculate the inventory of 0 when new and silver are selected separately. Third, calculate the inventory of 0 when both new – silver and new – silver are selected;

The union of the three attributes that should be grayed is the attribute that should be grayed when new – silver is selected

The basis of the calculation is indexKeyInfoMap. For example, in the second case, the index will be 0 when new and silver are selected separately. , its calculation process:

First, any combination of skU attributes with new or silver is two SKU attribute links of length 2, so select the data with key 1 from indexKeyInfoMap (indexKeyInfoMap[1]), Select key (6977_1081969, 6975_730004, 0) from this data for new and silver items respectively:

img

Then the 7 pieces of data are filtered out to obtain the set of SKU attributes that need to be gray, that is, in the key string of these data, the set of remaining values after removing the skU attributes currently selected, namely 6977_1081969 and 6975_730004

For example, for 72_1080628__6975_730004, 72_1080628 is left after processing, that is, the buttons corresponding to the SKU attribute with paramId being 72 and valueId being 1080628 should be gray, and other similar things

This gives you an array of items that need to be de-duplicated. Each item in this array is the skU property button that needs to be grayed when both new – silver are selected

The general solution is another algorithm, as described above: take n(n <= m) of m data, all possible picks, and then process each pick to calculate the skU attributes that have a zero inventory when combined with the skU combinations of each pick from the remaining skU attributes

@param arr; /** * @param arr; /** * @param arr; External calls do not need to pass this item * @param hasSeletedArr recursive use, external calls do not need to pass this item */function composeMN (m: number, n: number, arr: number[] = [], hasSeletedArr: number[] = [], rootArr: number[][] = []): number[][] {
  for (let i = 0; i < m; i++) {
    if (hasSeletedArr.includes(i)) continue
    hasSeletedArr = hasSeletedArr.slice()
    hasSeletedArr.push(i)
    let arr1 = arr.slice()
    arr1.push(i)
    if(n ! == 1) { composeMN(m, n - 1, arr1, hasSeletedArr, rootArr) }else {
      rootArr.push(arr1)
    }
  }
  return rootArr
}Copy the code

Of course, you can use whichever algorithm you think is better, but only if it solves the problem and is efficient enough

At this point, the process ends and the function completes

Algorithm to optimize

According to the above process, the function can be realized and the efficiency is not low. Normal business scenarios can be used completely, but there is still some room for optimization

A single SKU data cache with zero inventory

For a normal product, especially a platform directly operated product, in most cases, each SKU should have inventory. In this case, no matter which SKU attributes you select in any case, skU attributes should not be gray, because all SKUs have inventory. If I can be sure that all SKUs of a certain product are in stock, then there is no need to consider the grey button, and the part with the largest calculation can be completely eliminated, which reduces the calculation amount by more than half at a time

If not all SKU inventories are zero, there is room for optimization down to a single SKU attribute, as follows: Traverse the full SKU data set of all commodities, find out all SKU combinations with inventory 0, separate single SKU attributes from each combination, put all these individual SKU attributes into a set (called emptySkuIncludeList), then when the user selects any SKU combinations, Find that none of the skU attributes selected by the user are in the emptySkuIncludeList collection, and you can be sure that there will be no SKU attributes that need to be grayed out regardless of which SKU attributes the next user selects or deselects

This is easy to understand, for example, the skU combination with the current inventory of 0 is only 72_1080697__6975_730004__6977_1081969__7335_710006, that is, version: Japan and Korea, color: silver, finish color: new, configuration: Includelist [’72_1080697′, ‘6975_730004’, ‘6977_1081969’, ‘7335_710006’] When the user selects the SKU properties, the version is selected: Gold, corresponding to the ID combination 72_1080627 and 6975_730005, find that these two attributes are not in emptySkuIncludeList, so there is no need to predict the next step of the user, because no matter which SKU attribute the user will select or desist next, There are certainly no SKU attributes that need to be grayed

In other words, in this case, there is no need to perform the calculation of the ashy SKU attribute, which also reduces the amount of calculation by more than half

Finding array intersection

When calculating indexKeyInfoMap, for example, calculate the subscript set of data in the whole SKU data set corresponding to the SKU combination 72_1080627__6975_730003__6977_1080699 It is to search the subscript sets including 72_1080627, 6975_730003 and 6977_1080699 respectively from the whole SKU data set of goods, and then calculate the intersection of the three sets. 72_1080627__6975_730003__6977_1080699 is the subscript set of the SKU combination

So I start by finding the intersection of arrays, and I just compare them in pairs, and I iterate through one array, and THEN I look for the other array to see if it contains the item that I’m currently traversing, and if it does, it’s an intersection item

const resultArr = []
arr1.forEach(item => {
    if(arr2.includes(item)) {// Indicate the intersection item resultarr.push (item)}})Copy the code

The time complexity of this algorithm is about O(m * n). If the amount of data is large, it will still affect the efficiency. Therefore, I thought that since the data in ARR1 and ARR2 are arranged in ascending order, the algorithm complexity can be reduced to O(m)

functionintersectionSortArr (... params: Array<Array<number>>): Array<number> {if(! params || params.length === 0)return []
  if (params.length === 1) {
    return params[0]
  }
  let arr1 = params[0]
  let arr2 = params[1]
  if (params.length > 2) {
    returnintersectionSortArr(arr1, intersectionSortArr(arr2, ... params.slice(2))) }let arr = []
  if(! arr1.length || ! arr2.length || arr1[0] > arr2.slice(-1)[0] || arr2[0] > arr1.slice(-1)[0]) {return arr
  }
  let j = 0
  let k = 0
  let arr1Len = arr1.length
  let arr2Len = arr2.length
  while (j < arr1Len && k < arr2Len) {
    if (arr1[j] < arr2[k]) {
      j++
    } else if (arr1[j] > arr2[k]) {
      k++
    } else {
      arr.push(arr1[j])
      j++
      k++
    }
  }
  return arr
}Copy the code

Avoid regular expressions with high ambiguity

If you select some SKU attributes, you need to calculate the skU attributes to be grated. For example, if you select the SKU attributes 72_1080627 and 6975_730005, You need to go to indexKeyInfoMap[2] to find skU composite data that contains both skU attributes

That is, go to the data and look for:

img

So how to find the string containing both 72_1080627 and 6975_730005 in the format of 72_1080627 and 6975_730005? In general, you might want to match with a regular expression, as I started with this regular expression

reg = /[0-9_]*72_1080627[0-9_]*6975_730005[0-9_]*/Copy the code

Why do I write that? Because I only know that 72_1080627 is in front of 6975_730005 when sorting according to paramId, but IT is not clear which position 72_1080627 and 6975_730005 should be in, and whether there are other attributes connected before and after, so I wrote a very broad re. A regular fix, simple and easy

After writing it, I thought it was ok, but the problem was not big. Later, when the backend was tested in extreme cases, thousands of SKUs were issued for each product, and I found that the front end would be stuck for more than ten seconds before moving. I quickly checked and found that the regular took too long, just because the regular was too vague

There are also many optimization methods. For example, you can determine the position of paramId by selecting the user, and clearly know that 72_1080627 must be the first, 6975_730005 must be the second, and there must be a third, so that the regular can be accurately

reg = /^72_1080627__6975_730005[0-9_]+/Copy the code

This is obviously much more accurate and has fewer regular branches than the original fuzzy matching, but it still has to determine the position, and there will still be fuzzy matching, preferably without the regular

The selected skU attributes are not duplicated, and the data is ordered, so it is a string lookup, including or indexOf. However, there are some invalid lookups. The algorithm is on the order of m times n

Since the skU attributes selected by the user are sorted in ascending order according to paramId, an array is formed in sequence, such as [“72_1080627”, “6975_730005”], and then each string attribute of indexKeyInfoMap[2] is obtained. Also split the skU properties into arrays, such as [“72_1080627”, “6975_730003”, “6977_1080699”], and iterate over them:

const skuJoinArr = ["72 _1080627"."6975 _730003"."6977 _1080699"]
const currentSkuArr = ["72 _1080627"."6975 _730005"]
let i = 0
skuJoinArr.forEach(item => {
    if (item === currentSkuArr[i]) i++
})
if(I === skuJoinArr.length) {// Description Matched}Copy the code

Regardless of the SKU attributes selected by the user, the length of the attribute string of indexKeyInfoMap[index] I need to look for is only 1 longer than that selected by the user, and both are sorted in ascending order according to paramId, so as long as the two arrays step at the same time according to the conditions, SkuJoinArr contains currentSkuArr if the last, shorter currentSkuArr is stepped to the end

So we’re down to order m

, of course, there must be other places can be optimized, I now are clearly made the above a few, and optimization of this kind of thing, is not accurate to every line of every period of the whole project complete optimization is the best, no dead Angle for optimized code, often is not the same as written according to the normal thinking, business code a cost-effective, balanced development is the best

After adding the above optimization, the back-end extreme test was tested on Huawei P30. The initialization of more than 1000 SKUs only takes 200~300 ms, and the initialization of more than 600 SKUs only takes 70 ~ 80ms, which is dozens of times higher than the initial time of more than ten seconds

However, in practical application scenarios, for a normal product, the number of SKUs is generally not more than 100. For example, the iPhone XR on Jingdong now has less than 60 SKUs. The initial use time is about 20 ~ 30ms, so it is completely sufficient

Npm package

At the beginning, I did not intend to make this thing into a third-party package, because this thing is not a general tool or a general component, but a scenario component with strong business, which is highly coupled with the actual business scenario. Therefore, it is difficult to customize the third-party package according to the actual business scenario

But everyone seems to like the flattening of the complex code third-party libraries, only need to import can be out of the box, do not need to care about what internal logic, has more than one friend suggested that I will be packaged into NPM package, convenient promotion, so I want to the next, with part of the business interaction must be impossible as a separate package, but the core computing part can ah, Receive external input data, perform internal calculation, and return the calculation result. As for how to use the calculation result, it is the business side’s business, which is also a kind of withdrawal, right

So I based on this idea, made a NPM package, interested can try

Matters needing attention

As mentioned above, the data source needs to be sorted according to paramId. Normally, the field will be numeric (or a string of numeric types). Here, it is sorted according to paramId size, but there may be a problem. MAX_SAFE_INTEGER = MAX_SAFE_INTEGER = MAX_SAFE_INTEGER

Javascript has a concept of a maximum safe integer. This value is number. MAX_SAFE_INTEGER, and the constant value is 9007199254740991, which is 2 to the power of 53. Javascript uses the double-precision Floating-point format numbers specified in IEEE 754, where it is safe to represent numbers in the range [-(253-1), 253-1].

Safe here means being able to accurately represent integers and correctly compare integers. For example, number.max_safe_INTEGER + 1 === number.max_safe_INTEGER + 2 will return true, which is mathematically incorrect. See number.isSafeINTEGER () for more.

ParamsId = paramsId = paramsId = paramsId = paramsId = paramsId = paramsId = paramsId = paramsId = paramsId If you have confirmed with the back end, the value will certainly not exceed 9007199254740991. It would be best if there was no need to deal with it. But if you are not sure, it must be dealt with, depending on your actual scenario

conclusion

A began to think of ideas, for I will don’t know where laid a hand on him, because seems to be a lot of things to consider, consider to this have forgotten that, but really settled down to clear up the thoughts carefully, actually was more clear, thinking, thinking with the code can be implemented, this is relatively simple, as long as you don’t put yourself around the halo

As the saying goes, Talk is cheap, show me the code, some of the expressions in the article may not be clear, or the code is the most direct, so I also made an online Demo (preferably on the mobile terminal), the source code can also be put on Github, you can try it yourself