admin管理员组

文章数量:1025784

I tried the typical sort function and checked if item is string. But I get a very strange output. Tried multiple different approaches.

 var arr = [{section: '12.2.a'},
               {section: '12.2.b.iii'},
               {section: '12.2.c'},
               {section: '12'},
               {section: '12A'},
               {section: '12.3.b'},
               {section: '12.3.c'},
               {section: 'Q2'},
               {section: 'Q32'},
               {section: 'Q6'},
               {section: 'Q5'}]



var arr2 = arr.sort(function(a, b) {
    var nums1 = a.section.split(".");
    var nums2 = b.section.split(".");

    for (var i = 0; i < nums1.length; i++) {
      if (nums2[i]) {
        if (nums1[i] !== nums2[i]) {
          if (isNaN(parseInt(nums1[i])) && isNaN(parseInt(nums2[i]))) {
            return nums1[i].localeCompare(nums2[i]);
          }
          return parseInt(nums1[i]) - parseInt(nums2[i]);   
        }
      } else {
        return 1;
      }
    }
    return -1;
});

Should I use localeCompare or is it possible to without ? Would like the output to be:

[
 {section: '12'},
 {section: '12A'},
 {section: '12.2.a'},
 {section: '12.2.b.iii'},
 {section: '12.2.c'},
 {section: '12.3.b'},
 {section: '12.3.c'},
 {section: 'Q2'},
 {section: 'Q6'},
 {section: 'Q5'}
 {section: 'Q32'}]

Would much appreciate any suggestions

I tried the typical sort function and checked if item is string. But I get a very strange output. Tried multiple different approaches.

 var arr = [{section: '12.2.a'},
               {section: '12.2.b.iii'},
               {section: '12.2.c'},
               {section: '12'},
               {section: '12A'},
               {section: '12.3.b'},
               {section: '12.3.c'},
               {section: 'Q2'},
               {section: 'Q32'},
               {section: 'Q6'},
               {section: 'Q5'}]



var arr2 = arr.sort(function(a, b) {
    var nums1 = a.section.split(".");
    var nums2 = b.section.split(".");

    for (var i = 0; i < nums1.length; i++) {
      if (nums2[i]) {
        if (nums1[i] !== nums2[i]) {
          if (isNaN(parseInt(nums1[i])) && isNaN(parseInt(nums2[i]))) {
            return nums1[i].localeCompare(nums2[i]);
          }
          return parseInt(nums1[i]) - parseInt(nums2[i]);   
        }
      } else {
        return 1;
      }
    }
    return -1;
});

Should I use localeCompare or is it possible to without ? Would like the output to be:

[
 {section: '12'},
 {section: '12A'},
 {section: '12.2.a'},
 {section: '12.2.b.iii'},
 {section: '12.2.c'},
 {section: '12.3.b'},
 {section: '12.3.c'},
 {section: 'Q2'},
 {section: 'Q6'},
 {section: 'Q5'}
 {section: 'Q32'}]

Would much appreciate any suggestions

Share Improve this question asked Oct 20, 2016 at 9:06 ProzrachniyProzrachniy 1518 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 5

I propose a pletely different approach. We're going to modify your strings until they are sortable by localeCompare

Here's how:

// "12" sorts before "2", prefixing to "12" and "02" fixes this
// (length should be bigger than your largest nr)
var makeLength5 = prefixWithZero.bind(null, 5);

// This function generates a string that is sortable by localeCompare
var toSortableString = function(obj) {
  return obj.section
    .replace(/\./g, "z")            // Places `12A` before `12.` but after `12`
    .replace(/\d+/g, makeLength5);  // Makes every number the same length
};

var arr = [{section:"12.2.a"},{section:"12.2.b.iii"},{section:"12.2.c"},{section:"12"},{section:"12A"},{section:"12.3.b"},{section:"12.3.c"},{section:"Q2"},{section:"Q32"},{section:"Q6"},{section:"Q5"}];
var arr2 = arr.sort(function(a, b) {
  return toSortableString(a).localeCompare(toSortableString(b));
});

console.log(JSON.stringify(arr2.map(function(s){ return s.section; }), null, 2));

// Helper methods
function prefixWithZero(length, str) {
  while (str.length < length) {
    str = "0" + str;
  }
  return str;
};

You could split the string and use sorting with map, while paring each element of the one with each element of the other one. if both elements are numbers, take the difference, otherwise return the result of localeCompare.

Bonus: Sort with roman numbers.

function customSort(data, key, order) {

    function isNumber(v) {
        return (+v).toString() === v;
    }

    function isRoman(s) {
        // http://stackoverflow./a/267405/1447675
        return /^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/i.test(s);
    }

    function parseRoman(s) {
        var val = { M: 1000, D: 500, C: 100, L: 50, X: 10, V: 5, I: 1 };
        return s.toUpperCase().split('').reduce(function (r, a, i, aa) {
            return val[a] < val[aa[i + 1]] ? r - val[a] : r + val[a];
        }, 0);
    }

    var sort = {
            asc: function (a, b) {
                var i = 0,
                    l = Math.min(a.value.length, b.value.length);

                while (i < l && a.value[i] === b.value[i]) {
                    i++;
                }
                if (i === l) {
                    return a.value.length - b.value.length;
                }
                if (isNumber(a.value[i]) && isNumber(b.value[i])) {
                    return a.value[i] - b.value[i];
                }
                if (isRoman(a.value[i]) && isRoman(b.value[i])) {
                    return parseRoman(a.value[i]) - parseRoman(b.value[i]);
                }
                return a.value[i].localeCompare(b.value[i]);
            },
            desc: function (a, b) {
                return sort.asc(b, a);
            }
        },
        mapped = data.map(function (el, i) {
            var string = el[key].replace(/\d(?=[a-z])|[a-z](?=\.)/gi, '$&. .'),
                regex = /(\d+)|([^0-9.]+)/g,
                m,
                parts = [];

            while ((m = regex.exec(string)) !== null) {
                parts.push(m[0]);
            }
            return { index: i, value: parts, o: el, string: string };
        });

    mapped.sort(sort[order] || sort.asc);
    return mapped.map(function (el) {
        return data[el.index];
    });
}

var arr = [{ section: '12.2.a' }, { section: '12.2.b.viii' }, { section: '12.2.b.xi' }, { section: '12.2.b.x' }, { section: '12.2.b.ix' }, { section: '12.2.b.vii' }, { section: '12.2.b.vi' }, { section: '12.2.b.iv' }, { section: '12.2.b.v' }, { section: '12.2.b.ii' }, { section: '12.2.b.iii' }, { section: '12.2.b.i' }, { section: '12.2.b.iii' }, { section: '12.2.c' }, { section: '12' }, { section: '12A' }, { section: '12.3.b' }, { section: '12.3.c' }, { section: 'Q2' }, { section: 'Q32' }, { section: 'Q6' }, { section: 'Q5' }, { section: 'Q.10' }, { section: 'Q.1' }, { section: 'Q.2' }];

console.log('sorted array asc', customSort(arr, 'section'));
console.log('sorted array desc', customSort(arr, 'section', 'desc'));
console.log('original array', arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }

I tried the typical sort function and checked if item is string. But I get a very strange output. Tried multiple different approaches.

 var arr = [{section: '12.2.a'},
               {section: '12.2.b.iii'},
               {section: '12.2.c'},
               {section: '12'},
               {section: '12A'},
               {section: '12.3.b'},
               {section: '12.3.c'},
               {section: 'Q2'},
               {section: 'Q32'},
               {section: 'Q6'},
               {section: 'Q5'}]



var arr2 = arr.sort(function(a, b) {
    var nums1 = a.section.split(".");
    var nums2 = b.section.split(".");

    for (var i = 0; i < nums1.length; i++) {
      if (nums2[i]) {
        if (nums1[i] !== nums2[i]) {
          if (isNaN(parseInt(nums1[i])) && isNaN(parseInt(nums2[i]))) {
            return nums1[i].localeCompare(nums2[i]);
          }
          return parseInt(nums1[i]) - parseInt(nums2[i]);   
        }
      } else {
        return 1;
      }
    }
    return -1;
});

Should I use localeCompare or is it possible to without ? Would like the output to be:

[
 {section: '12'},
 {section: '12A'},
 {section: '12.2.a'},
 {section: '12.2.b.iii'},
 {section: '12.2.c'},
 {section: '12.3.b'},
 {section: '12.3.c'},
 {section: 'Q2'},
 {section: 'Q6'},
 {section: 'Q5'}
 {section: 'Q32'}]

Would much appreciate any suggestions

I tried the typical sort function and checked if item is string. But I get a very strange output. Tried multiple different approaches.

 var arr = [{section: '12.2.a'},
               {section: '12.2.b.iii'},
               {section: '12.2.c'},
               {section: '12'},
               {section: '12A'},
               {section: '12.3.b'},
               {section: '12.3.c'},
               {section: 'Q2'},
               {section: 'Q32'},
               {section: 'Q6'},
               {section: 'Q5'}]



var arr2 = arr.sort(function(a, b) {
    var nums1 = a.section.split(".");
    var nums2 = b.section.split(".");

    for (var i = 0; i < nums1.length; i++) {
      if (nums2[i]) {
        if (nums1[i] !== nums2[i]) {
          if (isNaN(parseInt(nums1[i])) && isNaN(parseInt(nums2[i]))) {
            return nums1[i].localeCompare(nums2[i]);
          }
          return parseInt(nums1[i]) - parseInt(nums2[i]);   
        }
      } else {
        return 1;
      }
    }
    return -1;
});

Should I use localeCompare or is it possible to without ? Would like the output to be:

[
 {section: '12'},
 {section: '12A'},
 {section: '12.2.a'},
 {section: '12.2.b.iii'},
 {section: '12.2.c'},
 {section: '12.3.b'},
 {section: '12.3.c'},
 {section: 'Q2'},
 {section: 'Q6'},
 {section: 'Q5'}
 {section: 'Q32'}]

Would much appreciate any suggestions

Share Improve this question asked Oct 20, 2016 at 9:06 ProzrachniyProzrachniy 1518 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 5

I propose a pletely different approach. We're going to modify your strings until they are sortable by localeCompare

Here's how:

// "12" sorts before "2", prefixing to "12" and "02" fixes this
// (length should be bigger than your largest nr)
var makeLength5 = prefixWithZero.bind(null, 5);

// This function generates a string that is sortable by localeCompare
var toSortableString = function(obj) {
  return obj.section
    .replace(/\./g, "z")            // Places `12A` before `12.` but after `12`
    .replace(/\d+/g, makeLength5);  // Makes every number the same length
};

var arr = [{section:"12.2.a"},{section:"12.2.b.iii"},{section:"12.2.c"},{section:"12"},{section:"12A"},{section:"12.3.b"},{section:"12.3.c"},{section:"Q2"},{section:"Q32"},{section:"Q6"},{section:"Q5"}];
var arr2 = arr.sort(function(a, b) {
  return toSortableString(a).localeCompare(toSortableString(b));
});

console.log(JSON.stringify(arr2.map(function(s){ return s.section; }), null, 2));

// Helper methods
function prefixWithZero(length, str) {
  while (str.length < length) {
    str = "0" + str;
  }
  return str;
};

You could split the string and use sorting with map, while paring each element of the one with each element of the other one. if both elements are numbers, take the difference, otherwise return the result of localeCompare.

Bonus: Sort with roman numbers.

function customSort(data, key, order) {

    function isNumber(v) {
        return (+v).toString() === v;
    }

    function isRoman(s) {
        // http://stackoverflow./a/267405/1447675
        return /^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/i.test(s);
    }

    function parseRoman(s) {
        var val = { M: 1000, D: 500, C: 100, L: 50, X: 10, V: 5, I: 1 };
        return s.toUpperCase().split('').reduce(function (r, a, i, aa) {
            return val[a] < val[aa[i + 1]] ? r - val[a] : r + val[a];
        }, 0);
    }

    var sort = {
            asc: function (a, b) {
                var i = 0,
                    l = Math.min(a.value.length, b.value.length);

                while (i < l && a.value[i] === b.value[i]) {
                    i++;
                }
                if (i === l) {
                    return a.value.length - b.value.length;
                }
                if (isNumber(a.value[i]) && isNumber(b.value[i])) {
                    return a.value[i] - b.value[i];
                }
                if (isRoman(a.value[i]) && isRoman(b.value[i])) {
                    return parseRoman(a.value[i]) - parseRoman(b.value[i]);
                }
                return a.value[i].localeCompare(b.value[i]);
            },
            desc: function (a, b) {
                return sort.asc(b, a);
            }
        },
        mapped = data.map(function (el, i) {
            var string = el[key].replace(/\d(?=[a-z])|[a-z](?=\.)/gi, '$&. .'),
                regex = /(\d+)|([^0-9.]+)/g,
                m,
                parts = [];

            while ((m = regex.exec(string)) !== null) {
                parts.push(m[0]);
            }
            return { index: i, value: parts, o: el, string: string };
        });

    mapped.sort(sort[order] || sort.asc);
    return mapped.map(function (el) {
        return data[el.index];
    });
}

var arr = [{ section: '12.2.a' }, { section: '12.2.b.viii' }, { section: '12.2.b.xi' }, { section: '12.2.b.x' }, { section: '12.2.b.ix' }, { section: '12.2.b.vii' }, { section: '12.2.b.vi' }, { section: '12.2.b.iv' }, { section: '12.2.b.v' }, { section: '12.2.b.ii' }, { section: '12.2.b.iii' }, { section: '12.2.b.i' }, { section: '12.2.b.iii' }, { section: '12.2.c' }, { section: '12' }, { section: '12A' }, { section: '12.3.b' }, { section: '12.3.c' }, { section: 'Q2' }, { section: 'Q32' }, { section: 'Q6' }, { section: 'Q5' }, { section: 'Q.10' }, { section: 'Q.1' }, { section: 'Q.2' }];

console.log('sorted array asc', customSort(arr, 'section'));
console.log('sorted array desc', customSort(arr, 'section', 'desc'));
console.log('original array', arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }

本文标签: