จะเปรียบเทียบหมายเลขรุ่นซอฟต์แวร์โดยใช้ js ได้อย่างไร (หมายเลขเท่านั้น)


164

นี่คือหมายเลขเวอร์ชั่นของซอฟต์แวร์:

"1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"

ฉันจะเปรียบเทียบสิ่งนี้ได้อย่างไร ?? สมมติว่าคำสั่งที่ถูกต้องคือ:

"1.0", "1.0.1", "2.0", "2.0.0.1", "2.0.1"

ความคิดนั้นง่าย ... : อ่านตัวเลขตัวแรก, มากกว่า, ที่สอง, หลังจากนั้นตัวที่สาม .... แต่ฉันไม่สามารถแปลงหมายเลขเวอร์ชั่นเป็นเลขทศนิยมได้ .... คุณยังสามารถดูหมายเลขเวอร์ชันได้เช่นกัน นี้:

"1.0.0.0", "1.0.1.0", "2.0.0.0", "2.0.0.1", "2.0.1.0"

และนี่คือความชัดเจนมากขึ้นเพื่อดูความคิดที่อยู่เบื้องหลัง ... แต่วิธีการแปลงเป็นโปรแกรมคอมพิวเตอร์ ?? มีใครมีความคิดเกี่ยวกับวิธีการเรียงลำดับนี้หรือไม่? ขอบคุณ.


5
นี่จะเป็นคำถามสัมภาษณ์ประเภท fizzbuzz ที่ดี
Steve Claridge

2
นี่คือสาเหตุที่หมายเลขรุ่นซอฟต์แวร์ทั้งหมดควรเป็นจำนวนเต็มเช่น 2001403 เมื่อคุณต้องการแสดงในรูปแบบที่เป็นมิตรเช่น "2.0.14.3" ดังนั้นคุณจึงจัดรูปแบบหมายเลขรุ่น ณ เวลานำเสนอ
jarmod

2
ปัญหาทั่วไปที่นี่เป็นการเปรียบเทียบ Semantic Version และไม่ใช่เรื่องเล็กน้อย (ดู # 11 ที่semver.org ) โชคดีที่มีห้องสมุดอย่างเป็นทางการสำหรับว่าที่versioner ความหมายสำหรับ NPM
Dan Dascalescu

1
พบสคริปต์ง่ายๆที่เปรียบเทียบ semvers
vsync

คำตอบ:


133

แนวคิดพื้นฐานในการทำการเปรียบเทียบนี้จะใช้Array.splitเพื่อรับอาร์เรย์ของชิ้นส่วนจากสตริงอินพุตจากนั้นเปรียบเทียบคู่ของส่วนต่าง ๆ จากสองอาร์เรย์ หากชิ้นส่วนไม่เท่ากันเรารู้ว่าเวอร์ชันใดมีขนาดเล็กกว่า

มีรายละเอียดที่สำคัญบางประการที่ควรทราบ:

  1. ควรเปรียบเทียบชิ้นส่วนในแต่ละคู่อย่างไร คำถามต้องการเปรียบเทียบตัวเลข แต่ถ้าเรามีสตริงรุ่นที่ไม่ได้ประกอบด้วยตัวเลขเพียงหลัก (เช่น "1.0a")
  2. จะเกิดอะไรขึ้นหากสตริงรุ่นหนึ่งมีส่วนต่างกว่าส่วนอื่น ๆ น่าจะเป็น "1.0" น่าจะน้อยกว่า "1.0.1" แต่จะเกี่ยวกับ "1.0.0"

นี่คือรหัสสำหรับการใช้งานที่คุณสามารถใช้โดยตรง (สรุปสาระสำคัญด้วยเอกสารประกอบ ):

function versionCompare(v1, v2, options) {
    var lexicographical = options && options.lexicographical,
        zeroExtend = options && options.zeroExtend,
        v1parts = v1.split('.'),
        v2parts = v2.split('.');

    function isValidPart(x) {
        return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x);
    }

    if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) {
        return NaN;
    }

    if (zeroExtend) {
        while (v1parts.length < v2parts.length) v1parts.push("0");
        while (v2parts.length < v1parts.length) v2parts.push("0");
    }

    if (!lexicographical) {
        v1parts = v1parts.map(Number);
        v2parts = v2parts.map(Number);
    }

    for (var i = 0; i < v1parts.length; ++i) {
        if (v2parts.length == i) {
            return 1;
        }

        if (v1parts[i] == v2parts[i]) {
            continue;
        }
        else if (v1parts[i] > v2parts[i]) {
            return 1;
        }
        else {
            return -1;
        }
    }

    if (v1parts.length != v2parts.length) {
        return -1;
    }

    return 0;
}

รุ่นนี้เปรียบเทียบส่วนต่าง ๆตามธรรมชาติไม่ยอมรับส่วนต่อท้ายของอักขระและถือว่า "1.7" มีขนาดเล็กกว่า "1.7.0" โหมดการเปรียบเทียบสามารถเปลี่ยนเป็นสตริงและเวอร์ชันที่สั้นกว่าสามารถเป็นศูนย์แบบอัตโนมัติโดยใช้อาร์กิวเมนต์ตัวเลือกที่สาม

มี JSFiddle ที่รัน "การทดสอบหน่วย" ที่นี่ ; มันเป็นเวอร์ชั่นที่ขยายออกมาเล็กน้อยของงานของripper234 (ขอบคุณ)

หมายเหตุสำคัญ:รหัสนี้ใช้Array.mapและArray.everyซึ่งหมายความว่าจะไม่ทำงานในเวอร์ชัน IE ที่เก่ากว่า 9 หากคุณต้องการสนับสนุนผู้ใช้เหล่านั้นคุณจะต้องจัดทำโพลีฟิลสำหรับวิธีการที่หายไป


16
นี่คือรุ่นปรับปรุงที่มีการทดสอบหน่วยบางอย่าง: jsfiddle.net/ripper234/Xv9WL/28
ripper234

5
เฮ้ทุกคนฉันได้รวมส่วนสำคัญนี้ไว้ใน gitrepo พร้อมการทดสอบและทุกอย่างและวางมันไว้ในเวลา 23.00 น. และขัดขืนเพื่อให้ฉันสามารถรวมไว้ในโครงการของฉันได้ง่ายขึ้น github.com/gabe0x02/version_compare
Gabriel Littman

2
@GabrielLittman: เฮ้ขอบคุณที่สละเวลาทำอย่างนั้น! อย่างไรก็ตามรหัสทั้งหมดบน SO นั้นได้รับลิขสิทธิ์ด้วยCC-BY-SAโดยค่าเริ่มต้น ซึ่งหมายความว่าคุณไม่สามารถมีแพ็คเกจของคุณได้รับใบอนุญาต GPL ฉันรู้ว่าการบัญญัติกฎหมายไม่ใช่สิ่งที่ทุกคนอยู่ที่นี่ แต่มันจะดีถ้าคุณแก้ไขมัน
Jon

2
@GabrielLittman: จริง ๆ แล้ว GPL มีข้อ จำกัด อย่างมากในแง่ที่ว่าคุณถูกบังคับให้ใช้สิทธิ์การใช้งาน GPL สำหรับรหัสทั้งหมดที่เข้ามาติดต่อกับรหัส GPL ที่มีอยู่ อย่างไรก็ตามสำหรับการอ้างอิงในอนาคตที่ดีและใช้กันอย่างแพร่หลาย "ทำสิ่งที่คุณต้องการสตริงไม่ติด" ใบอนุญาตเอ็มไอที
Jon


82

semver

parser รุ่น semantic ที่ใช้โดย npm

$ npm ติดตั้ง semver

var semver = require('semver');

semver.diff('3.4.5', '4.3.7') //'major'
semver.diff('3.4.5', '3.3.7') //'minor'
semver.gte('3.4.8', '3.4.7') //true
semver.ltr('3.4.8', '3.4.7') //false

semver.valid('1.2.3') // '1.2.3'
semver.valid('a.b.c') // null
semver.clean(' =v1.2.3 ') // '1.2.3'
semver.satisfies('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // true
semver.gt('1.2.3', '9.8.7') // false
semver.lt('1.2.3', '9.8.7') // true

var versions = [ '1.2.3', '3.4.5', '1.0.2' ]
var max = versions.sort(semver.rcompare)[0]
var min = versions.sort(semver.compare)[0]
var max = semver.maxSatisfying(versions, '*')

ลิงค์การกำหนดเวอร์ชันความหมาย :
https://www.npmjs.com/package/semver#prerelease-identifiers


8
ใช่. นี่คือคำตอบที่ถูกต้อง - การเปรียบเทียบรุ่นไม่สำคัญ (ดู # 11 ที่semver.org ) และมีไลบรารีระดับการผลิตที่ทำงาน
Dan Dascalescu

7
ในทางเทคนิคแล้วมันไม่ใช่คำตอบที่ถูกต้องเนื่องจาก node.js และ javascript นั้นแตกต่างกัน ฉันคิดว่าคำถามเดิมมีการกำหนดเป้าหมายเพิ่มเติมสำหรับเบราว์เซอร์ แต่ Google นำผมมาที่นี่และโชคดีที่ผมใช้โหนด :)
ลีแกรี่

2
NodeJS ไม่เพียง แต่เป็นโซลูชั่นด้านเซิร์ฟเวอร์เท่านั้น เฟรมเวิร์กอิเล็กตรอนฝัง nodeJS สำหรับแอปพลิเคชันเดสก์ท็อป นี่คือคำตอบที่ฉันต้องการ
Anthony Raymond

2
semver เป็นแพ็กเกจ npm สามารถใช้กับสภาพแวดล้อม JS ใด ๆ ! นี่คือคำตอบที่ถูกต้อง
neiker

4
@artuska เป็นอย่างดีจากนั้นก็ไปหาแพ็คเกจอื่นเช่นsemver-compar - 233B (น้อยกว่า 0.5kB!) gzipped
:)

50
// Return 1 if a > b
// Return -1 if a < b
// Return 0 if a == b
function compare(a, b) {
    if (a === b) {
       return 0;
    }

    var a_components = a.split(".");
    var b_components = b.split(".");

    var len = Math.min(a_components.length, b_components.length);

    // loop while the components are equal
    for (var i = 0; i < len; i++) {
        // A bigger than B
        if (parseInt(a_components[i]) > parseInt(b_components[i])) {
            return 1;
        }

        // B bigger than A
        if (parseInt(a_components[i]) < parseInt(b_components[i])) {
            return -1;
        }
    }

    // If one's a prefix of the other, the longer one is greater.
    if (a_components.length > b_components.length) {
        return 1;
    }

    if (a_components.length < b_components.length) {
        return -1;
    }

    // Otherwise they are the same.
    return 0;
}

console.log(compare("1", "2"));
console.log(compare("2", "1"));

console.log(compare("1.0", "1.0"));
console.log(compare("2.0", "1.0"));
console.log(compare("1.0", "2.0"));
console.log(compare("1.0.1", "1.0"));

ฉันคิดว่าบรรทัด: var len = Math.min(a_components.length, b_components.length);จะทำให้รุ่น 2.0.1.1 และ 2.0.1 ได้รับการปฏิบัติเหมือนกันไหม
Jon Egerton

1
ไม่ดูแลห่วง! ถ้าหนึ่งสายเป็นคำนำหน้าของอีก (เช่นห่วงถึงจุดสิ้นสุด) แล้วอีกสายหนึ่งจะถูกนำมาเป็นที่สูงขึ้น
Joe

บางทีคุณอาจจะถูกนำออกสะดุดของฉันมากกว่าภาษาอังกฤษในการแสดงความคิดเห็น ...
โจ

@ โจฉันรู้ว่ามันเป็นคำตอบที่ค่อนข้างเก่า แต่ฉันใช้ฟังก์ชั่นนี้ การทดสอบa = '7'และb = '7.0'ส่งคืน-1เนื่องจาก 7.0 นานขึ้น มีข้อเสนอแนะสำหรับสิ่งนั้นหรือไม่? ( console.log(compare("7", "7.0")); //returns -1)
RaphaelDDL

ฉันคิดว่ามาภายใต้หัวข้อของพฤติกรรมที่ไม่ได้กำหนด หากคุณมีหมายเลขเวอร์ชันเหล่านี้ฉันแน่ใจว่าคุณสามารถแก้ไขตรรกะเพื่อให้เหมาะกับความต้องการของคุณ
Joe

48

มีขนาดเล็กมากฟังก์ชั่นนี้ยังเปรียบเทียบอย่างรวดเร็วใช้เวลาหมายเลขรุ่นยาว ๆและขนาดจำนวนใด ๆ ต่อส่วน

คืนค่า:
- ตัวเลข< 0ถ้า a <b
- ตัวเลข> 0ถ้า a> b
- 0ถ้า a = b

ดังนั้นคุณสามารถใช้เป็นฟังก์ชั่นเปรียบเทียบสำหรับ Array.sort ();

แก้ไข:รุ่น Bugfixed ปอกศูนย์ต่อท้ายเพื่อรับรู้ "1" และ "1.0.0" เท่ากัน

function cmpVersions (a, b) {
    var i, diff;
    var regExStrip0 = /(\.0+)+$/;
    var segmentsA = a.replace(regExStrip0, '').split('.');
    var segmentsB = b.replace(regExStrip0, '').split('.');
    var l = Math.min(segmentsA.length, segmentsB.length);

    for (i = 0; i < l; i++) {
        diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10);
        if (diff) {
            return diff;
        }
    }
    return segmentsA.length - segmentsB.length;
}

// TEST
console.log(
['2.5.10.4159',
 '1.0.0',
 '0.5',
 '0.4.1',
 '1',
 '1.1',
 '0.0.0',
 '2.5.0',
 '2',
 '0.0',
 '2.5.10',
 '10.5',
 '1.25.4',
 '1.2.15'].sort(cmpVersions));
// Result:
// ["0.0.0", "0.0", "0.4.1", "0.5", "1.0.0", "1", "1.1", "1.2.15", "1.25.4", "2", "2.5.0", "2.5.10", "2.5.10.4159", "10.5"]


ล้มเหลวด้วย '0.0' และ '0.0.0' ดูซอ: jsfiddle.net/emragins/9e9pweqg
emragins

1
@emragins คุณต้องทำอะไรเมื่อไหร่
Skylar Ittner

1
@emragins: ฉันไม่เห็นว่ามันล้มเหลว มันแสดงผล["0.0.0", "0.0", "0.4.1", "0.5", "1.0.0", "1", "1.1", "1.2.15", "1.25.4", "2", "2.5.0", "2.5.10", "2.5.10.4159", "10.5"] เมื่อโค้ดของคุณออก["0.0", "0.0.0", "0.4.1", "0.5", "1", "1.0.0", "1.1", "1.2.15", "1.25.4", "2", "2.5.0", "2.5.10", "2.5.10.4159", "10.5"] มาซึ่งเหมือนกันอย่างสมบูรณ์ตั้งแต่ 0.0 และ 0.0.0 ถือว่าเท่ากันซึ่งหมายความว่าไม่เกี่ยวข้องว่า '0.0' ก่อน '0.0.0' หรือในทางกลับกัน
LeJared

ฉันยอมรับว่านี่เป็นจุดปกติ ฉันใช้สิ่งนี้กับgithub.com/jonmiles/bootstrap-treeviewซึ่งโหนด tiers ในลักษณะที่คล้ายคลึงกับเวอร์ชันมีเพียงโหนดโหนดแม่และลูกและดัชนีของพวกเขาเท่านั้น อดีต ผู้ปกครอง: 0.0, เด็ก: 0.0.0, 0.0.1 ดูรายละเอียดเพิ่มเติมเกี่ยวกับสาเหตุที่ฉันสนใจ: github.com/jonmiles/bootstrap-treeview/issues/251
emragins

1
ดูคำตอบที่นี่stackoverflow.com/questions/6611824/why-do-we-need-to-use-radix เบราว์เซอร์รุ่นเก่าที่ใช้ในการเดาพารามิเตอร์ Radix หากไม่ได้ระบุไว้ เลขศูนย์นำหน้าในสตริงตัวเลขเช่นส่วนตรงกลางใน "1.09.12" เคยถูกวิเคราะห์ด้วย radix = 8 ส่งผลให้หมายเลข 0 แทนที่จะเป็นหมายเลขที่คาดไว้ 9
LeJared

14

นำมาจากhttp://java.com/js/deployJava.js :

    // return true if 'installed' (considered as a JRE version string) is
    // greater than or equal to 'required' (again, a JRE version string).
    compareVersions: function (installed, required) {

        var a = installed.split('.');
        var b = required.split('.');

        for (var i = 0; i < a.length; ++i) {
            a[i] = Number(a[i]);
        }
        for (var i = 0; i < b.length; ++i) {
            b[i] = Number(b[i]);
        }
        if (a.length == 2) {
            a[2] = 0;
        }

        if (a[0] > b[0]) return true;
        if (a[0] < b[0]) return false;

        if (a[1] > b[1]) return true;
        if (a[1] < b[1]) return false;

        if (a[2] > b[2]) return true;
        if (a[2] < b[2]) return false;

        return true;
    }

ง่าย แต่ จำกัด เพียงสามฟิลด์รุ่น
Dan Dascalescu

11

ไม่พบฟังก์ชันที่ทำสิ่งที่ฉันต้องการที่นี่ ดังนั้นฉันจึงเขียนของตัวเอง นี่คือผลงานของฉัน ฉันหวังว่าบางคนจะพบว่ามีประโยชน์

ข้อดี:

  • จัดการสตริงรุ่นที่มีความยาวโดยพลการ '1' หรือ '1.1.1.1.1'

  • เริ่มต้นแต่ละค่าเป็น 0 หากไม่ได้ระบุ เพียงเพราะสายยาวไม่ได้หมายความว่ามันเป็นรุ่นที่ใหญ่กว่า ('1' ควรเหมือนกับ '1.0' และ '1.0.0.0')

  • เปรียบเทียบตัวเลขไม่ใช่สตริง ('3' <'21' ควรเป็นจริงไม่ใช่เท็จ)

  • ไม่ต้องเสียเวลากับการเปรียบเทียบที่ไร้ประโยชน์ในลูป (เปรียบเทียบเพื่อ ==)

  • คุณสามารถเลือกเครื่องมือเปรียบเทียบของคุณเอง

จุดด้อย:

  • มันไม่ได้จัดการกับตัวอักษรในสตริงรุ่น (ฉันไม่รู้ว่ามันจะทำงานยังไง)

รหัสของฉันคล้ายกับคำตอบที่ยอมรับโดยJon :

function compareVersions(v1, comparator, v2) {
    "use strict";
    var comparator = comparator == '=' ? '==' : comparator;
    if(['==','===','<','<=','>','>=','!=','!=='].indexOf(comparator) == -1) {
        throw new Error('Invalid comparator. ' + comparator);
    }
    var v1parts = v1.split('.'), v2parts = v2.split('.');
    var maxLen = Math.max(v1parts.length, v2parts.length);
    var part1, part2;
    var cmp = 0;
    for(var i = 0; i < maxLen && !cmp; i++) {
        part1 = parseInt(v1parts[i], 10) || 0;
        part2 = parseInt(v2parts[i], 10) || 0;
        if(part1 < part2)
            cmp = 1;
        if(part1 > part2)
            cmp = -1;
    }
    return eval('0' + comparator + cmp);
}

ตัวอย่าง :

compareVersions('1.2.0', '==', '1.2'); // true
compareVersions('00001', '==', '1.0.0'); // true
compareVersions('1.2.0', '<=', '1.2'); // true
compareVersions('2.2.0', '<=', '1.2'); // false

รุ่นนี้ในความคิดของฉันดีกว่าหนึ่งในคำตอบที่ได้รับการอนุมัติ!
user3807877

1
ฟังก์ชั่นนี้มีแนวโน้มที่จะฉีดรหัสถ้าใช้พารามิเตอร์ตัวเปรียบเทียบกับอินพุตของผู้ใช้ที่ไม่ถูกตรวจสอบ! ตัวอย่าง: comparVersions ('1.2', '== 0; alert ("cotcha");', '1.2');
LeJared

@LeJared จริง เมื่อฉันเขียนมันเราจะไม่ใช้มันกับรหัสที่ผู้ใช้ส่งไป น่าจะยกมันขึ้นมาได้ตามปกติ ฉันได้อัปเดตรหัสในขณะนี้เพื่อลดโอกาสที่จะเกิดขึ้นแล้ว แม้ว่าตอนนี้เมื่อผู้รวบรวมเว็บแพ็คและ node.js อื่น ๆ กลายเป็นที่แพร่หลายฉันขอแนะนำว่าคำตอบของโมฮัมเหม็ด Akdimข้างต้นโดยใช้ semver มักจะเป็นคำตอบที่ถูกต้องสำหรับคำถามนี้
Viktor

10

ฟังก์ชั่นที่ง่ายและสั้น:

function isNewerVersion (oldVer, newVer) {
  const oldParts = oldVer.split('.')
  const newParts = newVer.split('.')
  for (var i = 0; i < newParts.length; i++) {
    const a = parseInt(newParts[i]) || 0
    const b = parseInt(oldParts[i]) || 0
    if (a > b) return true
    if (a < b) return false
  }
  return false
}

แบบทดสอบ:

isNewerVersion('1.0', '2.0') // true
isNewerVersion('1.0', '1.0.1') // true
isNewerVersion('1.0.1', '1.0.10') // true
isNewerVersion('1.0.1', '1.0.1') // false
isNewerVersion('2.0', '1.0') // false
isNewerVersion('2', '1.0') // false
isNewerVersion('2.0.0.0.0.1', '2.1') // true
isNewerVersion('2.0.0.0.0.1', '2.0') // false

คุณสามารถทำให้มันง่ายขึ้นด้วย: const a = ~~ newParts [i]; ในความเป็นจริงนี่เป็นวิธีที่มีประสิทธิภาพมากที่สุดในการแปลงสตริงเป็นจำนวนเต็มซึ่งส่งคืนค่า 0 หากตัวแปรไม่ได้กำหนดหรือมีอักขระที่ไม่ใช่ตัวเลข
vanowm

5

ยกโทษให้ฉันถ้าความคิดนี้ได้รับการเข้าชมในลิงค์ที่ฉันไม่ได้เห็น

ฉันประสบความสำเร็จด้วยการเปลี่ยนชิ้นส่วนเป็นผลรวมถ่วงน้ำหนักดังนี้:

partSum = this.major * Math.Pow(10,9);
partSum += this.minor * Math.Pow(10, 6);
partSum += this.revision * Math.Pow(10, 3);
partSum += this.build * Math.Pow(10, 0);

ซึ่งทำให้การเปรียบเทียบง่ายมาก (เปรียบเทียบสองเท่า) ช่องรุ่นของเราจะไม่เกิน 4 หลัก

7.10.2.184  -> 7010002184.0
7.11.0.1385 -> 7011001385.0

ฉันหวังว่าสิ่งนี้จะช่วยใครบางคนเนื่องจากเงื่อนไขหลายอย่างดูเหมือนจะเกินความจริงเล็กน้อย


2
สิ่งนี้จะหยุดลงถ้านี่น้อยกว่า> 999 (จะทับด้วยหลัก)
Afanasii Kurakin

5

นี่คืออีกเวอร์ชั่นสั้นที่ใช้งานได้กับเวอร์ชันย่อยใด ๆ เลขศูนย์และเบาะเลขคู่ด้วยตัวอักษร (1.0.0b3)

function compareVer(a, b)
{
    //treat non-numerical characters as lower version
    //replacing them with a negative number based on charcode of each character
    function fix(s)
    {
        return "." + (s.toLowerCase().charCodeAt(0) - 2147483647) + ".";
    }
    a = ("" + a).replace(/[^0-9\.]/g, fix).split('.');
    b = ("" + b).replace(/[^0-9\.]/g, fix).split('.');
    var c = Math.max(a.length, b.length);
    for (var i = 0; i < c; i++)
    {
        //convert to integer the most efficient way
        a[i] = ~~a[i];
        b[i] = ~~b[i];
        if (a[i] > b[i])
            return 1;
        else if (a[i] < b[i])
            return -1;
    }
    return 0;
}

เอาท์พุท:

0 : a = b

1 : a> b

-1 : a <b

1.0.0.0.0.0 = 1.0
1.0         < 1.0.1
1.0b1       < 1.0
1.0a        < 1.0b
1.1         > 1.0.1b
1.1alpha    < 1.1beta
1.1rc1      > 1.1beta
1.0001      > 1.00000.1.0.0.0.01

https://jsfiddle.net/vanowm/p7uvtbor/


5

2017 คำตอบ:

v1 = '20.0.12'; 
v2 = '3.123.12';

compareVersions(v1,v2) 
// return positive: v1 > v2, zero:v1 == v2, negative: v1 < v2 
function compareVersions(v1, v2) {
        v1= v1.split('.')
        v2= v2.split('.')
        var len = Math.max(v1.length,v2.length)
        /*default is true*/
        for( let i=0; i < len; i++)
            v1 = Number(v1[i] || 0);
            v2 = Number(v2[i] || 0);
            if (v1 !== v2) return v1 - v2 ;
            i++;
        }
        return 0;
    }

รหัสที่ง่ายที่สุดสำหรับเบราว์เซอร์สมัยใหม่:

 function compareVersion2(ver1, ver2) {
      ver1 = ver1.split('.').map( s => s.padStart(10) ).join('.');
      ver2 = ver2.split('.').map( s => s.padStart(10) ).join('.');
      return ver1 <= ver2;
 }

ความคิดที่นี่คือการเปรียบเทียบตัวเลข แต่ในรูปแบบของสตริง เพื่อให้การเปรียบเทียบทำงานทั้งสองสตริงต้องมีความยาวเท่ากัน ดังนั้น:

"123" > "99"กลายเป็น"123" > "099"
ช่องว่างภายในจำนวนสั้น ๆ "แก้ไข" การเปรียบเทียบ

ที่นี่ฉันเติมแต่ละส่วนด้วยศูนย์ถึงความยาว 10 จากนั้นใช้สตริงเปรียบเทียบง่าย ๆ สำหรับคำตอบ

ตัวอย่าง:

var ver1 = '0.2.10', ver2=`0.10.2`
//become 
ver1 = '0000000000.0000000002.0000000010'
ver2 = '0000000000.0000000010.0000000002'
// then it easy to see that
ver1 <= ver2 // true

คุณจะอธิบายฟังก์ชั่นcompareVersion2ว่าเกิดอะไรขึ้นกันแน่?
Usman Wali

ดีแล้วคุณสามารถใช้substringแทนpadStartสำหรับการเข้ากันได้ดีคือ var zeros = "0000000000"; '0.2.32'.split('.').map( s => zeros.substring(0, zeros.length-s.length) + s ).join('.') จะทำให้คุณ0000000000.0000000002.0000000032:)
Usman Wali


4

คำตอบ verbose ของฉันน้อยกว่าคำตอบส่วนใหญ่ที่นี่

/**
 * Compare two semver versions. Returns true if version A is greater than
 * version B
 * @param {string} versionA
 * @param {string} versionB
 * @returns {boolean}
 */
export const semverGreaterThan = function(versionA, versionB){
  var versionsA = versionA.split(/\./g),
    versionsB = versionB.split(/\./g)
  while (versionsA.length || versionsB.length) {
    var a = Number(versionsA.shift()), b = Number(versionsB.shift())
    if (a == b)
      continue
    return (a > b || isNaN(b))
  }
  return false
}

1
คุณควรทำให้มันเป็นโมดูลและวางไว้บน node.js จนถึงตอนนี้ฉันกำลังขโมยรหัสของคุณพร้อมระบุแหล่งที่มาให้คุณ ขอบคุณสำหรับสิ่งนี้.
r3wt

3

แม้ว่าคำถามนี้มีแล้ว คำตอบจำนวนมาก แต่คำถามแต่ละข้อก็ส่งเสริมวิธีการแก้ปัญหาหลังบ้านของพวกเขาเองในขณะที่เรามีระบบนิเวศทั้งหมดของห้องสมุดทดสอบ (การต่อสู้) สำหรับเรื่องนี้

การค้นหาอย่างรวดเร็วเกี่ยวกับNPM , GitHub , X จะทำให้เรามี libs ที่น่ารักและฉันต้องการเรียกใช้บางส่วน:

semver-compareเป็น lib ที่มีน้ำหนักเบามาก (~ 230B) ซึ่งมีประโยชน์อย่างยิ่งหากคุณต้องการเรียงลำดับตามหมายเลขเวอร์ชันเมื่อเมธอดที่เปิดเผยของไลบรารีกลับ-1มา0หรือ1เหมาะสม

แกนกลางของ lib:

module.exports = function cmp (a, b) {
    var pa = a.split('.');
    var pb = b.split('.');
    for (var i = 0; i < 3; i++) {
        var na = Number(pa[i]);
        var nb = Number(pb[i]);
        if (na > nb) return 1;
        if (nb > na) return -1;
        if (!isNaN(na) && isNaN(nb)) return 1;
        if (isNaN(na) && !isNaN(nb)) return -1;
    }
    return 0;
};

compare-semver มีขนาดค่อนข้างใหญ่ (~ 4.4kB gzipped) แต่อนุญาตให้มีการเปรียบเทียบที่ไม่เหมือนใครบางอย่างเช่นหา min / max ของ stack ของรุ่นหรือเพื่อดูว่ารุ่นที่ให้มานั้นมีความพิเศษหรือน้อยกว่าอย่างอื่นในคอลเลกชันของ รุ่น

compare-versionsเป็นอีกหนึ่ง lib ขนาดเล็ก (~ 630B gzipped) และทำตามข้อกำหนดอย่างดีซึ่งหมายความว่าคุณสามารถเปรียบเทียบรุ่นที่มีการตั้งค่าสถานะ alpha / beta และแม้กระทั่ง wildcards (เช่นสำหรับรุ่นรอง / patch: 1.0.xหรือ1.0.*)

ประเด็นคือ: ไม่จำเป็นต้องคัดลอกโค้ดจาก StackOverflow เสมอไปหากคุณสามารถหารุ่นทดสอบ (หน่วย -) ที่เหมาะสมผ่านผู้จัดการแพ็คเกจที่คุณเลือก


3

ฉันพบปัญหาที่คล้ายกันและฉันได้สร้างวิธีแก้ไขปัญหาแล้ว รู้สึกอิสระที่จะลอง

ก็จะส่งกลับ0สำหรับequal, 1ถ้ารุ่นที่มีgreaterและ-1ถ้ามันเป็นless

function compareVersion(currentVersion, minVersion) {
  let current = currentVersion.replace(/\./g," .").split(' ').map(x=>parseFloat(x,10))
  let min = minVersion.replace(/\./g," .").split(' ').map(x=>parseFloat(x,10))

  for(let i = 0; i < Math.max(current.length, min.length); i++) {
    if((current[i] || 0) < (min[i] || 0)) {
      return -1
    } else if ((current[i] || 0) > (min[i] || 0)) {
      return 1
    }
  }
  return 0
}


console.log(compareVersion("81.0.1212.121","80.4.1121.121"));
console.log(compareVersion("81.0.1212.121","80.4.9921.121"));
console.log(compareVersion("80.0.1212.121","80.4.9921.121"));
console.log(compareVersion("4.4.0","4.4.1"));
console.log(compareVersion("5.24","5.2"));
console.log(compareVersion("4.1","4.1.2"));
console.log(compareVersion("4.1.2","4.1"));
console.log(compareVersion("4.4.4.4","4.4.4.4.4"));
console.log(compareVersion("4.4.4.4.4.4","4.4.4.4.4"));
console.log(compareVersion("0","1"));
console.log(compareVersion("1","1"));
console.log(compareVersion("1","1.0.00000.0000"));
console.log(compareVersion("","1"));
console.log(compareVersion("10.0.1","10.1"));


2

ความคิดคือการเปรียบเทียบสองรุ่นและรู้ว่าที่ใหญ่ที่สุด เราลบ "." และเราเปรียบเทียบแต่ละตำแหน่งของเวกเตอร์กับตำแหน่งอื่น

// Return 1  if a > b
// Return -1 if a < b
// Return 0  if a == b

function compareVersions(a_components, b_components) {

   if (a_components === b_components) {
       return 0;
   }

   var partsNumberA = a_components.split(".");
   var partsNumberB = b_components.split(".");

   for (var i = 0; i < partsNumberA.length; i++) {

      var valueA = parseInt(partsNumberA[i]);
      var valueB = parseInt(partsNumberB[i]);

      // A bigger than B
      if (valueA > valueB || isNaN(valueB)) {
         return 1;
      }

      // B bigger than A
      if (valueA < valueB) {
         return -1;
      }
   }
}

มหากาพย์คำตอบสิ่งที่ฉันกำลังมองหา
วินซ์

2
// Returns true if v1 is bigger than v2, and false if otherwise.
function isNewerThan(v1, v2) {
      v1=v1.split('.');
      v2=v2.split('.');
      for(var i = 0; i<Math.max(v1.length,v2.length); i++){
        if(v1[i] == undefined) return false; // If there is no digit, v2 is automatically bigger
        if(v2[i] == undefined) return true; // if there is no digit, v1 is automatically bigger
        if(v1[i] > v2[i]) return true;
        if(v1[i] < v2[i]) return false;
      }
      return false; // Returns false if they are equal
    }

1
ยินดีต้อนรับสู่ SO คำถามนี้มีคำตอบที่ดีอยู่แล้วโปรดงดเว้นการเพิ่มคำตอบใหม่เว้นแต่คุณจะเพิ่มคำตอบใหม่
ต่อ

1

replace()ฟังก์ชั่นเท่านั้นแทนที่เกิดขึ้นครั้งแรกในสตริง ดังนั้นให้แทนที่ด้วย. ,หลังจากนั้นลบทั้งหมด.และให้,การ.อีกครั้งและแยกมันลอย

for(i=0; i<versions.length; i++) {
    v = versions[i].replace('.', ',');
    v = v.replace(/\./g, '');
    versions[i] = parseFloat(v.replace(',', '.'));
}

ในที่สุดจัดเรียงมัน:

versions.sort();

1

ลองดูโพสต์บล็อกนี้ ฟังก์ชั่นนี้ใช้งานได้กับหมายเลขรุ่นที่เป็นตัวเลข

function compVersions(strV1, strV2) {
  var nRes = 0
    , parts1 = strV1.split('.')
    , parts2 = strV2.split('.')
    , nLen = Math.max(parts1.length, parts2.length);

  for (var i = 0; i < nLen; i++) {
    var nP1 = (i < parts1.length) ? parseInt(parts1[i], 10) : 0
      , nP2 = (i < parts2.length) ? parseInt(parts2[i], 10) : 0;

    if (isNaN(nP1)) { nP1 = 0; }
    if (isNaN(nP2)) { nP2 = 0; }

    if (nP1 != nP2) {
      nRes = (nP1 > nP2) ? 1 : -1;
      break;
    }
  }

  return nRes;
};

compVersions('10', '10.0'); // 0
compVersions('10.1', '10.01.0'); // 0
compVersions('10.0.1', '10.0'); // 1
compVersions('10.0.1', '10.1'); // -1

1

ตัวอย่างเช่นหากเราต้องการตรวจสอบว่ารุ่น jQuery ปัจจุบันน้อยกว่า 1.8 parseFloat($.ui.version) < 1.8 )จะให้ผลที่ไม่ถูกต้องหรือไม่หากรุ่นคือ "1.10.1" เนื่องจาก parseFloat ("1.10.1") กลับ1.1มา การเปรียบเทียบสตริงจะผิดไปตั้งแต่"1.8" < "1.10"ประเมินไปfalseประเมิน

ดังนั้นเราต้องการการทดสอบเช่นนี้

if(versionCompare($.ui.version, "1.8") < 0){
    alert("please update jQuery");
}

ฟังก์ชั่นต่อไปนี้จัดการสิ่งนี้อย่างถูกต้อง:

/** Compare two dotted version strings (like '10.2.3').
 * @returns {Integer} 0: v1 == v2, -1: v1 < v2, 1: v1 > v2
 */
function versionCompare(v1, v2) {
    var v1parts = ("" + v1).split("."),
        v2parts = ("" + v2).split("."),
        minLength = Math.min(v1parts.length, v2parts.length),
        p1, p2, i;
    // Compare tuple pair-by-pair. 
    for(i = 0; i < minLength; i++) {
        // Convert to integer if possible, because "8" > "10".
        p1 = parseInt(v1parts[i], 10);
        p2 = parseInt(v2parts[i], 10);
        if (isNaN(p1)){ p1 = v1parts[i]; } 
        if (isNaN(p2)){ p2 = v2parts[i]; } 
        if (p1 == p2) {
            continue;
        }else if (p1 > p2) {
            return 1;
        }else if (p1 < p2) {
            return -1;
        }
        // one operand is NaN
        return NaN;
    }
    // The longer tuple is always considered 'greater'
    if (v1parts.length === v2parts.length) {
        return 0;
    }
    return (v1parts.length < v2parts.length) ? -1 : 1;
}

นี่คือตัวอย่างบางส่วน:

// compare dotted version strings
console.assert(versionCompare("1.8",      "1.8.1")    <   0);
console.assert(versionCompare("1.8.3",    "1.8.1")    >   0);
console.assert(versionCompare("1.8",      "1.10")     <   0);
console.assert(versionCompare("1.10.1",   "1.10.1")   === 0);
// Longer is considered 'greater'
console.assert(versionCompare("1.10.1.0", "1.10.1")   >   0);
console.assert(versionCompare("1.10.1",   "1.10.1.0") <   0);
// Strings pairs are accepted
console.assert(versionCompare("1.x",      "1.x")      === 0);
// Mixed int/string pairs return NaN
console.assert(isNaN(versionCompare("1.8", "1.x")));
//works with plain numbers
console.assert(versionCompare("4", 3)   >   0);

ดูที่นี่สำหรับตัวอย่างสดและชุดทดสอบ: http://jsfiddle.net/mar10/8KjvP/


สวัสดีเพิ่งสังเกตเห็นว่า ripper234 ได้โพสต์ URL ซอในความคิดเห็นเมื่อไม่กี่เดือนที่ผ่านมาที่ค่อนข้างคล้ายกัน อย่างไรก็ตามฉันตอบคำตอบของฉันที่นี่ ...
มี.ค. 10

อันนี้ก็จะล้มเหลว (เป็นตัวแปรส่วนใหญ่) ในกรณีเหล่านี้: versionCompare ('1.09', '1.1') ส่งคืน "1" เช่นเดียวกับ versionCompare ('1.702', '1.8')
shaman.sir

รหัสประเมิน "1.09"> "1.1" และ "1.702"> "1.8" ซึ่งฉันคิดว่าถูกต้อง หากคุณไม่เห็นด้วยคุณสามารถชี้ไปที่แหล่งข้อมูลที่แสดงความคิดเห็นของคุณหรือไม่
มี.ค.

มันขึ้นอยู่กับหลักการของคุณ - เพราะฉันรู้ว่าไม่มีกฎหรืออะไรที่เข้มงวด เกี่ยวกับทรัพยากรบทความวิกิพีเดียสำหรับ "การกำหนดเวอร์ชันซอฟต์แวร์" ใน "การเพิ่มขึ้นของลำดับ" กล่าวว่า 1.81 อาจเป็นรุ่นรองของ 1.8 ดังนั้น 1.8 ควรอ่านเป็น 1.80 บทความการกำหนดเวอร์ชันเชิงความหมายsemver.org/spec/v2.0.0.htmlยังกล่าวอีกว่า 1.9.0 -> 1.10.0 -> 1.11.0 ดังนั้น 1.9.0 จึงถือว่าเป็น 1.90.0 ในการเปรียบเทียบเช่นนี้ ดังนั้นตามตรรกะนี้รุ่น 1.702 จึงอยู่ก่อนรุ่น 1.8 ซึ่งถือว่าเป็น 1.800
shaman.sir

1
ฉันเห็นว่ากฎบางข้อปฏิบัติต่อ 1.8 <1.81 <1.9 แต่ใน semver คุณจะใช้ 1.8.1 แทน 1.81 Semver (ตามที่ฉันเข้าใจ) ถูกกำหนดไว้รอบ ๆ ข้อสันนิษฐานว่าการเพิ่มส่วนจะสร้างรุ่น 'ภายหลัง' เสมอดังนั้น 1.8 <1.8.1 <1.9 <1.10 <1.81 <1.90 <1.100 ฉันไม่เห็นสิ่งบ่งชี้ว่าสิ่งนี้ จำกัด ไว้เพียงสองหลัก ดังนั้นฉันจะบอกว่ารหัสของฉันเป็นไปตาม semver อย่างสมบูรณ์
mar10

1

นี่คือการใช้ coffeescript ที่เหมาะสำหรับใช้กับ Array.sort ซึ่งได้แรงบันดาลใจจากคำตอบอื่น ๆ ที่นี่:

# Returns > 0 if v1 > v2 and < 0 if v1 < v2 and 0 if v1 == v2
compareVersions = (v1, v2) ->
  v1Parts = v1.split('.')
  v2Parts = v2.split('.')
  minLength = Math.min(v1Parts.length, v2Parts.length)
  if minLength > 0
    for idx in [0..minLength - 1]
      diff = Number(v1Parts[idx]) - Number(v2Parts[idx])
      return diff unless diff is 0
  return v1Parts.length - v2Parts.length

นี้เป็นแรงบันดาลใจจากคำตอบของ LeJared
Dan Dascalescu

สิ่งนี้ทำงานไม่ถูกต้อง .. นี่คือผลลัพธ์ .. ผลลัพธ์ ['1.1.1', '2.1.1', '3.3.1.0', '3.1.1.0']
ertan2002

1

ฉันเขียนโมดูลโหนดสำหรับการเรียงลำดับเวอร์ชันคุณสามารถค้นหาได้ที่นี่: version-sort

คุณสมบัติ :

  • ไม่มีการ จำกัด ลำดับของงาน '1.0.1.5.53.54654.114.1.154.45'
  • ไม่จำกัดความยาวของลำดับ: '1.1546515465451654654654654654138754431574364321353734'
  • สามารถเรียงลำดับวัตถุตามรุ่น (ดู README)
  • สเตจ (เช่นอัลฟ่า, เบต้า, rc1, rc2)

อย่าลังเลที่จะเปิดปัญหาหากคุณต้องการคุณสมบัติอื่น


1

สิ่งนี้ใช้ได้กับรุ่นที่เป็นตัวเลขของความยาวใด ๆ คั่นด้วยจุด มันจะคืนค่าเป็นจริงก็ต่อเมื่อ myVersion คือ> = MinimumVersion ทำให้การสันนิษฐานว่ารุ่น 1 น้อยกว่า 1.0 รุ่น 1.1 น้อยกว่า 1.1.0 เป็นต้น มันค่อนข้างง่ายที่จะเพิ่มเงื่อนไขพิเศษเช่นการยอมรับตัวเลข (เพียงแปลงเป็นสตริง) และเลขฐานสิบหกหรือทำให้ตัวคั่นแบบไดนามิก (เพียงเพิ่มพารามิเตอร์ตัวคั่นจากนั้นแทนที่ "." ด้วยพารามิเตอร์)

function versionCompare(myVersion, minimumVersion) {

    var v1 = myVersion.split("."), v2 = minimumVersion.split("."), minLength;   

    minLength= Math.min(v1.length, v2.length);

    for(i=0; i<minLength; i++) {
        if(Number(v1[i]) > Number(v2[i])) {
            return true;
        }
        if(Number(v1[i]) < Number(v2[i])) {
            return false;
        }           
    }

    return (v1.length >= v2.length);
}

นี่คือการทดสอบบางอย่าง:

console.log(versionCompare("4.4.0","4.4.1"));
console.log(versionCompare("5.24","5.2"));
console.log(versionCompare("4.1","4.1.2"));
console.log(versionCompare("4.1.2","4.1"));
console.log(versionCompare("4.4.4.4","4.4.4.4.4"));
console.log(versionCompare("4.4.4.4.4.4","4.4.4.4.4"));
console.log(versionCompare("0","1"));
console.log(versionCompare("1","1"));
console.log(versionCompare("","1"));
console.log(versionCompare("10.0.1","10.1"));

อีกวิธีหนึ่งที่นี่เป็นรุ่นเรียกซ้ำ

function versionCompare(myVersion, minimumVersion) {
  return recursiveCompare(myVersion.split("."),minimumVersion.split("."),Math.min(myVersion.length, minimumVersion.length),0);
}

function recursiveCompare(v1, v2,minLength, index) {
  if(Number(v1[index]) < Number(v2[index])) {
    return false;
  }
  if(Number(v1[i]) < Number(v2[i])) {
    return true;
    }
  if(index === minLength) {
    return (v1.length >= v2.length);
  }
  return recursiveCompare(v1,v2,minLength,index+1);
}

1

ฉันค้นหาวิธีที่ง่ายที่สุดในการเปรียบเทียบพวกเขาไม่แน่ใจว่าเป็นสิ่งที่คุณต้องการหรือไม่ เมื่อฉันเรียกใช้โค้ดด้านล่างในคอนโซลมันสมเหตุสมผลและใช้ sort () วิธีฉันจะได้รับเรียงแถวของสตริงรุ่น มันขึ้นอยู่กับลำดับตัวอักษร

"1.0" < "1.0.1" //true
var arr = ["1.0.1", "1.0", "3.2.0", "1.3"]
arr.sort();     //["1.0", "1.0.1", "1.3", "3.2.0"]

3
มันใช้งานไม่ได้กับหมายเลขรุ่นสองหลักเช่น 1.10.0
Leukipp

1

คุณสามารถใช้String#localeCompareกับoptions

ความไว

ความแตกต่างในสตริงควรนำไปสู่ค่าผลลัพธ์ที่ไม่ใช่ศูนย์ ค่าที่เป็นไปได้คือ:

  • "base": เฉพาะสตริงที่แตกต่างในตัวอักษรฐานเปรียบเทียบไม่เท่ากัน ตัวอย่าง: a ≠ b, a = á,a = A ,
  • "accent": เฉพาะสตริงที่แตกต่างในตัวอักษรฐานหรือสำเนียงและเครื่องหมายกำกับอื่น ๆ เปรียบเทียบว่าไม่เท่ากัน a ≠ bตัวอย่าง: a ≠ á, a = A,
  • "case": เฉพาะสตริงที่แตกต่างกันในตัวอักษรฐานหรือตัวพิมพ์เปรียบเทียบเป็นไม่เท่ากัน a ≠ bตัวอย่าง: a = á, a ≠ A,
  • "variant": สตริงที่แตกต่างกันในตัวอักษรฐานสำเนียงและเครื่องหมายกำกับอื่น ๆ หรือกรณีเปรียบเทียบไม่เท่ากัน ความแตกต่างอื่น ๆ อาจถูกนำมาพิจารณาด้วย a ≠ bตัวอย่าง: a ≠ á, a ≠ A,

ค่าเริ่มต้นคือ "ตัวแปร" สำหรับการใช้งาน "เรียงลำดับ"; มันขึ้นอยู่กับสถานที่สำหรับการใช้งาน "ค้นหา"

เป็นตัวเลข

ไม่ว่าจะเป็นการเรียงลำดับตัวเลขควรใช้เช่น "1" <"2" <"10" ค่าที่เป็นไปได้คือtrueและfalse; falseเริ่มต้นคือ ตัวเลือกนี้สามารถตั้งค่าผ่านคุณสมบัติตัวเลือกหรือผ่านคีย์ส่วนขยาย Unicode หากมีการจัดเตรียมไว้ทั้งคู่optionsทรัพย์สินจะมีความสำคัญกว่า การใช้งานไม่จำเป็นต้องมีเพื่อสนับสนุนคุณสมบัตินี้

var versions = ["2.0.1", "2.0", "1.0", "1.0.1", "2.0.0.1"];

versions.sort((a, b) => a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' }));

console.log(versions);


มันใช้งานได้จริงอย่างไร? คืออะไรundefinedเหนือ, ภาษา? ทำไมคุณถึงโพสต์สิ่งนี้ในขณะที่ฉันอ่านคนอื่น ๆ ;)
mplungjan

undefinedเป็นส่วนของโลแคลไม่ได้ใช้ที่นี่
Nina Scholz

0

คุณไม่สามารถแปลงเป็นตัวเลขแล้วเรียงลำดับตามขนาดได้หรือไม่ ผนวก 0's กับตัวเลขที่มีความยาว <4

เล่นในคอนโซล:

$(["1.0.0.0", "1.0.1.0", "2.0.0.0", "2.0.0.1", "2.0.1", "3.0"]).each(function(i,e) {
    var n =   e.replace(/\./g,"");
    while(n.length < 4) n+="0" ; 
    num.push(  +n  )
});

รุ่นที่ใหญ่กว่าจำนวนที่มากกว่า แก้ไข: อาจต้องปรับเป็นบัญชีสำหรับซีรี่ส์รุ่นที่ใหญ่กว่า


นั่นเป็นเพียงตัวอย่างเมื่อเขาต้องทำบางสิ่งด้วยตัวเอง: P แทนที่จะเป็น 4 ได้รับจำนวนตัวเลขที่ใหญ่ที่สุดจากนั้นเติมตัวเลขที่ต่ำกว่าด้วย 0's
Contra

0

นี่เป็นกลลวงที่เรียบร้อย หากคุณกำลังจัดการกับค่าตัวเลขระหว่างช่วงของค่าเฉพาะคุณสามารถกำหนดค่าให้กับแต่ละระดับของวัตถุเวอร์ชัน ตัวอย่างเช่น "hugeValue" ถูกตั้งค่าเป็น 0xFF ที่นี่ซึ่งจะสร้างรูปแบบ "IP" เรียงตามลำดับการสร้างของคุณ

นอกจากนี้ยังรองรับการกำหนดเวอร์ชันที่เป็นตัวอักษรและตัวเลข (เช่น 1.2a <1.2b)

// The version compare function
function compareVersion(data0, data1, levels) {
    function getVersionHash(version) {
        var value = 0;
        version = version.split(".").map(function (a) {
            var n = parseInt(a);
            var letter = a.replace(n, "");
            if (letter) {
                return n + letter[0].charCodeAt() / 0xFF;
            } else {
                return n;
            }
        });
        for (var i = 0; i < version.length; ++i) {
            if (levels === i) break;
            value += version[i] / 0xFF * Math.pow(0xFF, levels - i + 1);
        }
        return value;
    };
    var v1 = getVersionHash(data0);
    var v2 = getVersionHash(data1);
    return v1 === v2 ? -1 : v1 > v2 ? 0 : 1;
};
// Returns 0 or 1, correlating to input A and input B
// Direct match returns -1
var version = compareVersion("1.254.253", "1.254.253a", 3);

0

ฉันชอบรุ่นจาก@ mar10แต่จากมุมมองของฉันมีโอกาสที่จะมีการหลอกลวง (ดูเหมือนว่าไม่ใช่กรณีที่รุ่นเข้ากันได้กับเอกสารการกำหนดเวอร์ชันความหมายแต่อาจเป็นกรณีที่ใช้ "หมายเลขสร้าง" ):

versionCompare( '1.09', '1.1');  // returns 1, which is wrong:  1.09 < 1.1
versionCompare('1.702', '1.8');  // returns 1, which is wrong: 1.702 < 1.8

ปัญหานี่คือตัวเลขย่อยของหมายเลขเวอร์ชันในบางกรณีเขียนด้วยศูนย์ต่อท้ายถูกตัดออก (อย่างน้อยที่สุดเมื่อฉันเห็นมันในขณะที่ใช้ซอฟต์แวร์ที่แตกต่างกัน) ซึ่งคล้ายกับส่วนที่มีเหตุผลของตัวเลขดังนั้น:

5.17.2054 > 5.17.2
5.17.2 == 5.17.20 == 5.17.200 == ... 
5.17.2054 > 5.17.20
5.17.2054 > 5.17.200
5.17.2054 > 5.17.2000
5.17.2054 > 5.17.20000
5.17.2054 < 5.17.20001
5.17.2054 < 5.17.3
5.17.2054 < 5.17.30

อย่างไรก็ตามหมายเลขย่อยเวอร์ชันแรก (หรือทั้งที่หนึ่งและสอง) จะถูกใช้เป็นค่าจำนวนเต็มเสมอ

หากคุณใช้การกำหนดเวอร์ชันประเภทนี้คุณอาจเปลี่ยนเพียงไม่กี่บรรทัดในตัวอย่าง:

// replace this:
p1 = parseInt(v1parts[i], 10);
p2 = parseInt(v2parts[i], 10);
// with this:
p1 = i/* > 0 */ ? parseFloat('0.' + v1parts[i], 10) : parseInt(v1parts[i], 10);
p2 = i/* > 0 */ ? parseFloat('0.' + v2parts[i], 10) : parseInt(v2parts[i], 10);

ดังนั้นทุกหมายเลขย่อยยกเว้นอันแรกจะถูกเปรียบเทียบเป็นทุ่นดังนั้น09และ1จะกลายเป็น0.09และ0.1ตามและเปรียบเทียบอย่างถูกต้องด้วยวิธีนี้ 2054และ3จะกลายเป็น0.2054และ0.3และ

เวอร์ชันที่สมบูรณ์นั้นคือ (เครดิตถึง@ mar10 ):

/** Compare two dotted version strings (like '10.2.3').
 * @returns {Integer} 0: v1 == v2, -1: v1 < v2, 1: v1 > v2
 */
function versionCompare(v1, v2) {
    var v1parts = ("" + v1).split("."),
        v2parts = ("" + v2).split("."),
        minLength = Math.min(v1parts.length, v2parts.length),
        p1, p2, i;
    // Compare tuple pair-by-pair. 
    for(i = 0; i < minLength; i++) {
        // Convert to integer if possible, because "8" > "10".
        p1 = i/* > 0 */ ? parseFloat('0.' + v1parts[i], 10) : parseInt(v1parts[i], 10);;
        p2 = i/* > 0 */ ? parseFloat('0.' + v2parts[i], 10) : parseInt(v2parts[i], 10);
        if (isNaN(p1)){ p1 = v1parts[i]; } 
        if (isNaN(p2)){ p2 = v2parts[i]; } 
        if (p1 == p2) {
            continue;
        }else if (p1 > p2) {
            return 1;
        }else if (p1 < p2) {
            return -1;
        }
        // one operand is NaN
        return NaN;
    }
    // The longer tuple is always considered 'greater'
    if (v1parts.length === v2parts.length) {
        return 0;
    }
    return (v1parts.length < v2parts.length) ? -1 : 1;
}

PS มันช้าลง แต่ก็เป็นไปได้ที่จะคิดเกี่ยวกับการใช้ฟังก์ชั่นการเปรียบเทียบเดียวกันอีกครั้งในการดำเนินงานความจริงที่ว่าสตริงนั้นเป็นอาร์เรย์ของตัวอักษร:

 function cmp_ver(arr1, arr2) {
     // fill the tail of the array with smaller length with zeroes, to make both array have the same length
     while (min_arr.length < max_arr.length) {
         min_arr[min_arr.lentgh] = '0';
     }
     // compare every element in arr1 with corresponding element from arr2, 
     // but pass them into the same function, so string '2054' will act as
     // ['2','0','5','4'] and string '19', in this case, will become ['1', '9', '0', '0']
     for (i: 0 -> max_length) {
         var res = cmp_ver(arr1[i], arr2[i]);
         if (res !== 0) return res;
     }
 }

0

ฉันทำสิ่งนี้ตามแนวคิด Kons และปรับให้เหมาะสมสำหรับรุ่น Java "1.7.0_45" มันเป็นเพียงฟังก์ชั่นที่มีความหมายในการแปลงสตริงเวอร์ชันเป็นทุ่น นี่คือฟังก์ชั่น:

function parseVersionFloat(versionString) {
    var versionArray = ("" + versionString)
            .replace("_", ".")
            .replace(/[^0-9.]/g, "")
            .split("."),
        sum = 0;
    for (var i = 0; i < versionArray.length; ++i) {
        sum += Number(versionArray[i]) / Math.pow(10, i * 3);
    }
    console.log(versionString + " -> " + sum);
    return sum;
}

สตริง "1.7.0_45" ถูกแปลงเป็น 1.0070000450000001 และนี่ดีพอสำหรับการเปรียบเทียบปกติ อธิบายข้อผิดพลาดได้ที่นี่: วิธีจัดการกับความแม่นยำของจำนวนจุดลอยตัวใน JavaScript . หากจำเป็นต้องใช้มากขึ้นแล้ว 3 หลักในส่วนใด ๆ Math.pow(10, i * 3);ที่คุณสามารถเปลี่ยนการแบ่ง

ผลลัพธ์จะมีลักษณะดังนี้:

1.7.0_45         > 1.007000045
ver 1.7.build_45 > 1.007000045
1.234.567.890    > 1.23456789

0

ฉันมีปัญหาในการเปรียบเทียบรุ่นเดียวกัน แต่อาจมีรุ่นที่มีสิ่งใดสิ่งหนึ่ง (เช่น: ตัวคั่นที่ไม่ใช่จุดส่วนขยายเช่น rc1, rc2 ... )

ฉันใช้สิ่งนี้ซึ่งโดยทั่วไปแล้วแบ่งสตริงรุ่นเป็นตัวเลขและไม่ใช่ตัวเลขและพยายามเปรียบเทียบตามประเภท

function versionCompare(a,b) {
  av = a.match(/([0-9]+|[^0-9]+)/g)
  bv = b.match(/([0-9]+|[^0-9]+)/g)
  for (;;) {
    ia = av.shift();
    ib = bv.shift();
    if ( (typeof ia === 'undefined') && (typeof ib === 'undefined') ) { return 0; }
    if (typeof ia === 'undefined') { ia = '' }
    if (typeof ib === 'undefined') { ib = '' }

    ian = parseInt(ia);
    ibn = parseInt(ib);
    if ( isNaN(ian) || isNaN(ibn) ) {
      // non-numeric comparison
      if (ia < ib) { return -1;}
      if (ia > ib) { return 1;}
    } else {
      if (ian < ibn) { return -1;}
      if (ian > ibn) { return 1;}
    }
  }
}

มีสมมติฐานบางอย่างที่นี่สำหรับบางกรณีเช่น: "1.01" === "1.1" หรือ "1.8" <"1.71" มันล้มเหลวในการจัดการ "1.0.0-rc.1" <"1.0.0" ตามที่ระบุโดย Semantic versionning 2.0.0


0

การประมวลผลรุ่นก่อนหน้าการจัดเรียงล่วงหน้าหมายความว่าการแยกวิเคราะห์จะไม่ถูกเรียกหลาย ๆ ครั้งโดยไม่จำเป็น การใช้ Array # map คล้ายกับข้อเสนอของ Michael Deal นี่เป็นประเภทที่ฉันใช้เพื่อค้นหาเซเวอร์มาตรฐานส่วนที่ 3 ใหม่ล่าสุด:

var semvers = ["0.1.0", "1.0.0", "1.1.0", "1.0.5"];

var versions = semvers.map(function(semver) {
    return semver.split(".").map(function(part) {
        return parseInt(part);
    });
});

versions.sort(function(a, b) {
    if (a[0] < b[0]) return 1;
    else if (a[0] > b[0]) return -1;
    else if (a[1] < b[1]) return 1;
    else if (a[1] > b[1]) return -1;
    else if (a[2] < b[2]) return 1;
    else if (a[2] > b[2]) return -1;
    return 0;
});

var newest = versions[0].join(".");
console.log(newest); // "1.1.0"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.