Interviewer: how to sort the array of string version numbers?

Time:2021-7-25

Interviewer: how to sort the array of string version numbers?

There is a classic interview question in segment fault:

There is a set of version numbers as follows [‘0.1.1’, ‘2.3.3’, ‘0.302.1’, ‘4.2’, ‘4.3.5’, ‘4.3.4.5’].

Now you need to sort them. The sorting results are [‘4.3.5’, ‘4.3.4.5’, ‘2.3.3’, ‘0.302.1’, ‘0.1.1’]

Question link

amongzzgzzg00The main idea of your answer is as follows, which is very concise and interesting:

const arr=['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5'];
arr.sort((a,b)=>a>b?-1:1);
console.log(arr); // ['4.3.5','4.3.4.5','2.3.3','0.302.1','0.1.1']

So the question comes:

Why can strings be easily sorted?

In JavaScript, strings are undoubtedly comparable. Guess what the output of the following code is?

console.log('5'>'1')
console.log('5'>'10')

The answer istruetrue

Comparison strings compare their Unicode values

This is because when comparing two strings, Unicode values based on the standard dictionary are used for comparison. adoptString.prototype.codePointAt()Method we can get the Unicode value of the string. therefore'5'>'1'The result istrue;

When the string length is greater than 1, the comparison is performed bit by bit, so'5'>'10'When comparing, first compare the first, that is'5'>'1', if there is a result, it will be returned. If there is no result, it will continue to compare the second bit. therefore'5'>'10'Results and'5'>'1'Same, tootrue

Looking back, it is not difficult to understand:.The Unicode value of is 46,0The Unicode value of is 48, and other numbers are incremented on this basis. So when comparing10.1Is greater than1.1Yes.

String comparison method has a small scope of application

The above explains why the case in the title can be realized through string comparison. But if you are smart, you will find that this comparison is problematic: if you modify the ARR in the title as follows:

const arr=[
    '0.5.1',
    '0.1.1',
    '2.3.3',
    '0.302.1',
    '4.2',
    '4.3.5',
    '4.3.4.5'
];

The string comparison method will make an error: the expected version number'0.302.1'Should be greater than'0.5.1'But the result of actual comparison is the opposite, becauseBit by bit comparison

It is not necessary to prepare the digit number for each version of the comparison condition, so it does not meet the condition of 1. So is there a more applicable and concise comparison method?

“Large number” weighting method

Compare NPM rule version number

It is assumed that the version number follows the NPM semantic rule, that is, the version number is determined byMAJOR.MINOR.PATCHIt consists of several parts:

const arr=['2.3.3', '4.3.4', '0.3.1'];

The target version number to be compared is obtained by the following formula:

MAJOR*p2 + MINOR*p + PATCH

The code is as follows:

const p = 1000;
const gen = (arr) => 
    arr.split('.').reduce(reducer,0);

const reducer = (acc,value,index) => 
    acc+(+value)*Math.pow(p,arr.length-index-1);

arr.sort((a,b)=> gen(a)>gen(b)?-1:1);

console.log(arr)

amongpIs a constant, and its value must be greater thanMAJOR/MINOR/PATCHThe maximum of the three is at least one order of magnitude. For example, the version number to be compared is1.0.1'0.302.1', ifpIf the value is 10, the calculated result will obviously not meet the expectation. andptake1000It can avoid pollution after weighting each sub version.

Similarly, there are version numbers of similar rules (e.g'1.0.1.12')Can be sorted by the above method.

More version numbers

If the version number array is as follows:

const arr=[
    '1.1',
    '2.3.3',
    '4.3.5',
    '0.3.1',
    '0.302.1',
    '4.20.0',
    '4.3.5.1',
    '1.2.3.4.5'
];

The above array not only does not followMajor.minor.patch gaugeThen, there is no obvious rule for its length. How to compare it?

It can be extended on the basis of fixed rule comparison. First, you need to obtain the maximum number of sub version numbers in the version number arraymaxLen。 Here we passMath.max()obtain:

const maxLen = Math.max(
    ...arr.map((item)=>item.split('.').length)
);

GetmaxLenThen you can override the reducer method:


const reducer = (acc,value,index) => 
    acc+(+value)*Math.pow(p,maxLen-index-1);

const gen = (arr) =>
    arr.split('.').reduce(reducer,0);

arr.sort((a,b)=> gen(a)>gen(b)?-1:1);

console.log(arr)

The above method is sufficient for the comparison of conventional version numbers. However, we know that the number type of JavaScript is double precision 64 bit floating-point type, ifmaxLenFor example, if the timestamp of each sub version is too large, the result of the above method is not accurate.

howeverBigIntThe proposal has entered the stage3 specification, which can represent any large integer. It can be predicted that in the near future, we do not need to consider the impact of the version number value range.

Cyclic comparison method

Compared with string comparison method and large number weighting method, circular comparison method is more applicable. The idea is still to compare the sub version number bit by bit: if the current version number is the same, compare the next bit; If the digits of the version number are not equal and the values of the first few digits are the same, it is considered that the version number with more digits is large.

The code is as follows:

arr.sort((a, b) => {
    let i = 0;
    const arr1 = a.split('.');
    const arr2 = b.split('.');

    while (true) {
        const s1 = arr1[i];
        const s2 = arr2[i++];

        if (s1 === undefined || s2 === undefined) {
            return arr2.length - arr1.length;
        }

        if (s1 === s2) continue;

        return s2 - s1;
    }
});

console.log(arr)

reflection

We have summarized and compared several methods for comparing version numbers. You can choose the appropriate method in different scenarios:

  • String comparison method
  • Large number weighting method
  • Cyclic comparison method

However, we know that the version number of software in a production environment usually does not consist entirely of arrays. For example, we can publish on NPM, such as1.0.0-betaperhaps6.0.0-alphaHow to compare the version numbers of packages in other formats? I believe that smart and diligent you must have your own ideas. You might as well leave a message for discussion.

Interviewer: how to sort the array of string version numbers?