用点、字母、数字对对象数组进行排序。我能够按数字排序,但混合值很困难。不确定是不是可以做对

Posted

技术标签:

【中文标题】用点、字母、数字对对象数组进行排序。我能够按数字排序,但混合值很困难。不确定是不是可以做对【英文标题】:Sort array of objects with dots, letters, numbers. I was able to sort by numbers, but mixed values are difficult. Not sure if possible to do it right用点、字母、数字对对象数组进行排序。我能够按数字排序,但混合值很困难。不确定是否可以做对 【发布时间】:2017-03-02 04:37:44 【问题描述】:

我尝试了典型的排序功能并检查项目是否为字符串。但我得到一个非常奇怪的输出。尝试了多种不同的方法。

 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;
);

我应该使用 localeCompare 还是可以不使用? 希望输出是:

[
 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']

非常感谢任何建议

【问题讨论】:

【参考方案1】:

您可以拆分字符串并使用sorting with map,同时将一个字符串的每个元素与另一个字符串的每个元素进行比较。如果两个元素都是数字,则取差,否则返回localeCompare的结果。

奖励:按罗马数字排序。

function customSort(data, key, order) 

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

    function isRoman(s) 
        // http://***.com/a/267405/1447675
        return /^M0,4(CM|CD|D?C0,3)(XC|XL|L?X0,3)(IX|IV|V?I0,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; 

【讨论】:

【参考方案2】:

我提出了一种完全不同的方法。 我们将修改您的字符串,直到它们可以按 localeCompare 排序

方法如下:

// "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;
;

【讨论】:

非常感谢!这太棒了! 免责声明:只有出现在字符串末尾且小于 IX 的罗马数字才会正确排序 我想用这个答案展示的是,一个快速的测试驱动实现可能会让你用更简单的代码完成 99% 的工作。不确定您是否可以在生产代码中摆脱它,但这是一个有趣的练习。特别是如果像您的问题一样,您不是将需求作为规范列表而是作为测试数据的集合(“从 thisthat”)。

以上是关于用点、字母、数字对对象数组进行排序。我能够按数字排序,但混合值很困难。不确定是不是可以做对的主要内容,如果未能解决你的问题,请参考以下文章

除数字外,如何按字母顺序对对象数组进行排序?

按字母数字对javascript数组进行排序[重复]

按数字(最高优先)然后按字母(字母顺序)对对象集合进行排序

按升序或降序对数字或字母进行通用排序

当字符串为数字时,如何在考虑值的同时按字母顺序对字符串进行排序?

当字符串是数字时,如何在考虑值的同时按字母顺序对字符串进行排序?