如何在多列上对数组进行排序?

时间:2010-05-06 20:27:04

标签: javascript algorithm sorting

我有一个多维数组。主数组是

的数组
[publicationID][publication_name][ownderID][owner_name] 

我要做的是按owner_name然后按publication_name对数组进行排序。我知道在JavaScript中你有Array.sort(),你可以在其中放置一个自定义函数,在我的情况下我有:

function mysortfunction(a, b) {
    var x = a[3].toLowerCase();
    var y = b[3].toLowerCase();

    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
}

这对于只对一列进行排序很好,即owner_name,但如何修改它以对owner_name进行排序,然后对publication_name进行排序?

16 个答案:

答案 0 :(得分:143)

如果所有者名称不同,请按其排序。否则,请使用发布名称作为决胜局。

function mysortfunction(a, b) {

  var o1 = a[3].toLowerCase();
  var o2 = b[3].toLowerCase();

  var p1 = a[1].toLowerCase();
  var p2 = b[1].toLowerCase();

  if (o1 < o2) return -1;
  if (o1 > o2) return 1;
  if (p1 < p2) return -1;
  if (p1 > p2) return 1;
  return 0;
}

答案 1 :(得分:53)

我认为您正在寻找的是然后他们:https://github.com/Teun/thenBy.js

它允许您使用标准的Array.sort,但使用firstBy().thenBy().thenBy()样式。

An example can be seen here

答案 2 :(得分:23)

需要按键进行SQL样式的混合asc和desc对象数组排序。

kennebec上面的解决方案帮助我实现了这个目标:

Array.prototype.keySort = function(keys) {

keys = keys || {};

// via
// https://stackoverflow.com/questions/5223/length-of-javascript-object-ie-associative-array
var obLen = function(obj) {
    var size = 0, key;
    for (key in obj) {
        if (obj.hasOwnProperty(key))
            size++;
    }
    return size;
};

// avoiding using Object.keys because I guess did it have IE8 issues?
// else var obIx = function(obj, ix){ return Object.keys(obj)[ix]; } or
// whatever
var obIx = function(obj, ix) {
    var size = 0, key;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) {
            if (size == ix)
                return key;
            size++;
        }
    }
    return false;
};

var keySort = function(a, b, d) {
    d = d !== null ? d : 1;
    // a = a.toLowerCase(); // this breaks numbers
    // b = b.toLowerCase();
    if (a == b)
        return 0;
    return a > b ? 1 * d : -1 * d;
};

var KL = obLen(keys);

if (!KL)
    return this.sort(keySort);

for ( var k in keys) {
    // asc unless desc or skip
    keys[k] = 
            keys[k] == 'desc' || keys[k] == -1  ? -1 
          : (keys[k] == 'skip' || keys[k] === 0 ? 0 
          : 1);
}

this.sort(function(a, b) {
    var sorted = 0, ix = 0;

    while (sorted === 0 && ix < KL) {
        var k = obIx(keys, ix);
        if (k) {
            var dir = keys[k];
            sorted = keySort(a[k], b[k], dir);
            ix++;
        }
    }
    return sorted;
});
return this;
};

样本用法:

var obja = [
  {USER:"bob",  SCORE:2000, TIME:32,    AGE:16, COUNTRY:"US"},
  {USER:"jane", SCORE:4000, TIME:35,    AGE:16, COUNTRY:"DE"},
  {USER:"tim",  SCORE:1000, TIME:30,    AGE:17, COUNTRY:"UK"},
  {USER:"mary", SCORE:1500, TIME:31,    AGE:19, COUNTRY:"PL"},
  {USER:"joe",  SCORE:2500, TIME:33,    AGE:18, COUNTRY:"US"},
  {USER:"sally",    SCORE:2000, TIME:30,    AGE:16, COUNTRY:"CA"},
  {USER:"yuri", SCORE:3000, TIME:34,    AGE:19, COUNTRY:"RU"},
  {USER:"anita",    SCORE:2500, TIME:32,    AGE:17, COUNTRY:"LV"},
  {USER:"mark", SCORE:2000, TIME:30,    AGE:18, COUNTRY:"DE"},
  {USER:"amy",  SCORE:1500, TIME:29,    AGE:19, COUNTRY:"UK"}
];

var sorto = {
  SCORE:"desc",TIME:"asc", AGE:"asc"
};

obja.keySort(sorto);

产生以下结果:

 0: {     USER: jane;     SCORE: 4000;    TIME: 35;       AGE: 16;    COUNTRY: DE;   }
 1: {     USER: yuri;     SCORE: 3000;    TIME: 34;       AGE: 19;    COUNTRY: RU;   }
 2: {     USER: anita;    SCORE: 2500;    TIME: 32;       AGE: 17;    COUNTRY: LV;   }
 3: {     USER: joe;      SCORE: 2500;    TIME: 33;       AGE: 18;    COUNTRY: US;   }
 4: {     USER: sally;    SCORE: 2000;    TIME: 30;       AGE: 16;    COUNTRY: CA;   }
 5: {     USER: mark;     SCORE: 2000;    TIME: 30;       AGE: 18;    COUNTRY: DE;   }
 6: {     USER: bob;      SCORE: 2000;    TIME: 32;       AGE: 16;    COUNTRY: US;   }
 7: {     USER: amy;      SCORE: 1500;    TIME: 29;       AGE: 19;    COUNTRY: UK;   }
 8: {     USER: mary;     SCORE: 1500;    TIME: 31;       AGE: 19;    COUNTRY: PL;   }
 9: {     USER: tim;      SCORE: 1000;    TIME: 30;       AGE: 17;    COUNTRY: UK;   }
 keySort: {  }

(使用here中的打印功能)

here is a jsbin example

编辑:cleaned up and posted as mksort.js on github

答案 3 :(得分:18)

对许多字符串字段进行排序的好方法是使用toLocaleCompare和布尔运算符||

类似的东西:

// Sorting record releases by name and then by title.
releases.sort((oldRelease, newRelease) => {
  const compareName = oldRelease.name.localeCompare(newRelease.name);
  const compareTitle = oldRelease.title.localeCompare(newRelease.title);

  return compareName || compareTitle;
})

如果你想对更多的字段进行排序,你可以简单地将它们从带有更多布尔运算符的return语句中链接起来。

答案 4 :(得分:17)

这对于各种尺寸的alpha种类都很方便。 按顺序将要排序的索引作为参数传递给它。

Array.prototype.deepSortAlpha= function(){
    var itm, L=arguments.length, order=arguments;

    var alphaSort= function(a, b){
        a= a.toLowerCase();
        b= b.toLowerCase();
        if(a== b) return 0;
        return a> b? 1:-1;
    }
    if(!L) return this.sort(alphaSort);

    this.sort(function(a, b){
        var tem= 0,  indx=0;
        while(tem==0 && indx<L){
            itm=order[indx];
            tem= alphaSort(a[itm], b[itm]); 
            indx+=1;        
        }
        return tem;
    });
    return this;
}

var arr= [[ "Nilesh","Karmshil"], ["Pranjal","Deka"], ["Susants","Ghosh"],
["Shiv","Shankar"], ["Javid","Ghosh"], ["Shaher","Banu"], ["Javid","Rashid"]];

arr.deepSortAlpha(1,0);

答案 5 :(得分:8)

您可以将2个变量连接成一个排序键,并将其用于比较。

list.sort(function(a,b){
   var aCat = a.var1 + a.var2;
   var bCat = b.var1 + b.var2;
   return (aCat > bCat ? 1 : aCat < bCat ? -1 : 0);
});

答案 6 :(得分:8)

我建议使用内置的比较器,并将所需的排序顺序与逻辑或||链接。

function customSort(a, b) {
    return a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]);
}

工作示例:

var array = [
    [0, 'Aluminium', 0, 'Francis'],
    [1, 'Argon', 1, 'Ada'],
    [2, 'Brom', 2, 'John'],
    [3, 'Cadmium', 3, 'Marie'],
    [4, 'Fluor', 3, 'Marie'],
    [5, 'Gold', 1, 'Ada'],
    [6, 'Kupfer', 4, 'Ines'],
    [7, 'Krypton', 4, 'Joe'],
    [8, 'Sauerstoff', 3, 'Marie'],
    [9, 'Zink', 5, 'Max']
];

array.sort(function (a, b) {
    return a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]);
});

document.write('<pre>');
array.forEach(function (a) {
    document.write(JSON.stringify(a) + '<br>');
});

答案 7 :(得分:4)

我找到了ASCII table。这是一个简单,功能强大且用于多重排序的小型库。我需要使用动态排序标准对一组对象进行排序:

const criteria = ['name', 'speciality']
const data = [
  { name: 'Mike', speciality: 'JS', age: 22 },
  { name: 'Tom', speciality: 'Java', age: 30 },
  { name: 'Mike', speciality: 'PHP', age: 40 },
  { name: 'Abby', speciality: 'Design', age: 20 },
]

const sorted = multisort(data, criteria)

console.log(sorted)
<script src="https://cdn.rawgit.com/peterkhayes/multisort/master/multisort.js"></script>

这个库更强大,这是我的情况。试试吧。

答案 8 :(得分:2)

我正在使用ng-grid并且需要对从API返回的记录数组进行多列排序,因此我想出了这个漂亮的动态多重排序函数。

首先,ng-grid会发生&#34;事件&#34; for&#34; ngGridSorted&#34;然后传回这个结构,描述排序:

sortData = {
    columns:    DOM Element,
    directions: [], //Array of string values desc or asc. Each index relating to the same index of fields
    fields:     [], //Array of string values
};

所以我构建了一个函数,它将动态生成基于sortData的排序函数,如上所示(不要被滚动条吓到!它只有50左右!线条很长!另外,我对这个斜坡感到抱歉。它阻止了水平滚动条!):

function SortingFunction(sortData)
{
    this.sortData = sortData;

    this.sort = function(a, b)
    {
        var retval = 0;

        if(this.sortData.fields.length)
        {
            var i = 0;

            /*
                Determine if there is a column that both entities (a and b)
                have that are not exactly equal. The first one that we find
                will be the column we sort on. If a valid column is not
                located, then we will return 0 (equal).
            */
            while(  (   !a.hasOwnProperty(this.sortData.fields[i]) 
                    ||  !b.hasOwnProperty(this.sortData.fields[i]) 
                    ||  (a.hasOwnProperty(this.sortData.fields[i]) 
                        && b.hasOwnProperty(this.sortData.fields[i]) 
                        && a[this.sortData.fields[i]] === b[this.sortData.fields[i]])
                    ) && i < this.sortData.fields.length){
                i++;
            }

            if(i < this.sortData.fields.length)
            {
                /*
                    A valid column was located for both entities
                    in the SortData. Now perform the sort.
                */
                if(this.sortData.directions 
                && i < this.sortData.directions.length 
                && this.sortData.directions[i] === 'desc')
                {
                    if(a[this.sortData.fields[i]] > b[this.sortData.fields[i]])
                        retval = -1;
                    else if(a[this.sortData.fields[i]] < b[this.sortData.fields[i]])
                        retval = 1;
                }
                else
                {
                    if(a[this.sortData.fields[i]] < b[this.sortData.fields[i]])
                        retval = -1;
                    else if(a[this.sortData.fields[i]] > b[this.sortData.fields[i]])
                        retval = 1;
                }
            }
        }

        return retval;
    }.bind(this);
}

然后我按照这样的方式对我的API(results)的结果进行排序:

results.sort(new SortingFunction(sortData).sort);

我希望别人能像我一样享受这个解决方案!谢谢!

答案 9 :(得分:2)

字符串追加方法

您可以简单地通过将多个值附加到字符串中并比较字符串来对多个值进行排序。添加分割键字符以防止从一个键到另一个键的径流很有帮助。

示例

const arr = [ 
    { a: 1, b: 'a', c: 3 },
    { a: 2, b: 'a', c: 5 },
    { a: 1, b: 'b', c: 4 },
    { a: 2, b: 'a', c: 4 }
]


function sortBy (arr, keys, splitKeyChar='~') {
    return arr.sort((i1,i2) => {
        const sortStr1 = keys.reduce((str, key) => str + splitKeyChar+i1[key], '')
        const sortStr2 = keys.reduce((str, key) => str + splitKeyChar+i2[key], '')
        return sortStr1.localeCompare(sortStr2)
    })
}

console.log(sortBy(arr, ['a', 'b', 'c']))

递归方法

您还可以使用递归来执行此操作。它比字符串追加方法复杂一点,但是它允许您在键级别上执行ASC和DESC。我对每个部分都进行了评论,因为它比较复杂。

有一些注释掉的测试可以显示和验证排序和默认顺序的混合工作。

示例

const arr = [ 
    { a: 1, b: 'a', c: 3 },
    { a: 2, b: 'a', c: 5 },
    { a: 1, b: 'b', c: 4 },
    { a: 2, b: 'a', c: 4 }
]


function sortBy (arr, keys) {
    return arr.sort(function sort (i1,i2, sKeys=keys) {
        // Get order and key based on structure
        const compareKey = (sKeys[0].key) ? sKeys[0].key : sKeys[0];
        const order = sKeys[0].order || 'ASC'; // ASC || DESC
        // Calculate compare value and modify based on order
        let compareValue = i1[compareKey].toString().localeCompare(i2[compareKey].toString())
        compareValue = (order.toUpperCase() === 'DESC') ? compareValue * -1 : compareValue
        // See if the next key needs to be considered 
        const checkNextKey = compareValue === 0 && sKeys.length !== 1
        // Return compare value
        return (checkNextKey) ? sort(i1, i2, sKeys.slice(1)): compareValue;
    })
}

// console.log(sortBy(arr, ['a', 'b', 'c']))
console.log(sortBy(arr, [{key:'a',order:'desc'}, 'b', 'c']))
// console.log(sortBy(arr, ['a', 'b', {key:'c',order:'desc'}]))
// console.log(sortBy(arr, ['a', {key:'b',order:'desc'}, 'c']))
// console.log(sortBy(arr, [{key:'a',order:'asc'}, {key:'b',order:'desc'}, {key:'c',order:'desc'}]))

答案 10 :(得分:1)

function multiSort() {

    var args =$.makeArray( arguments ),
        sortOrder=1, prop='', aa='',  b='';

    return function (a, b) {

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

         if(args[i][0]==='-'){
            prop=args[i].substr(1)
            sortOrder=-1
         }
         else{sortOrder=1; prop=args[i]}

         aa = a[prop].toLowerCase()
         bb = b[prop].toLowerCase()

         if (aa < bb) return -1 * sortOrder;
         if (aa > bb) return 1 * sortOrder;

       }

       return 0
    }

}
empArray.sort(multiSort( 'lastname','firstname')) Reverse with '-lastname'

答案 11 :(得分:1)

从某些虚拟DOM h函数合成的输出显示内存池块时,我遇到了类似的问题。基本上,我面临着与多标准数据排序相同的问题,例如来自全球玩家的得分结果。

我注意到多标准排序是:

port=5000

如果您不在乎,您可能会在if-else嵌套地狱中快速失败,就像应许的回调地狱...

如果我们编写一个“谓词”函数来确定替代项的哪个部分使用该怎么办?谓词很简单:

- sort by the first column
- if equal, sort by the second
- if equal, sort by the third
-  etc... nesting and nesting if-else

现在,在编写了分类测试(byCountrySize,byAge,byGameType,byScore,byLevel ...)之后,无论谁需要,您都可以对测试进行加权(1 = asc,-1 = desc,0 =禁用),然后进行测试在一个数组中,并应用如下简化的“ decide”函数:

// useful for chaining test
const decide = (test, other) => test === 0 ? other : test

瞧!由您决定自己的标准/权重/订单...但是您可以理解。希望这会有所帮助!

编辑:  *确保每一列都有一个总的排序顺序  *请注意,列顺序之间没有依赖关系,也没有循环依赖关系

如果不是这样,排序可能会不稳定!

答案 12 :(得分:1)

尝试一下:

t.sort( (a,b)=> a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]) );

let t = [
    //[publicationID, publication_name, ownderID, owner_name ]
    [1, 'ZBC', 3, 'John Smith'],
    [2, 'FBC', 5, 'Mike Tyson'],
    [3, 'ABC', 7, 'Donald Duck'],
    [4, 'DBC', 1, 'Michael Jackson'],
    [5, 'XYZ', 2, 'Michael Jackson'],
    [6, 'BBC', 4, 'Michael Jackson'],
  ]; 
  
  // owner_name subarrray index = 3
  // publication_name subarrray index = 1

t.sort( (a,b)=> a[3].localeCompare(b[3]) || a[1].localeCompare(b[1]) );

console.log(t.join('\n'));

我假设您的数据位于数组let t = [ [publicationID, publication_name, ownderID, owner_name ], ... ]中,其中owner_name的索引= 3,publication_name = 1的索引。

答案 13 :(得分:1)

我刚刚向npm发布了一个名为sort-helper source on github的微库。想法是通过语法by导入帮助器sort来为items.sort(by(column, ...otherColumns))数组方法创建比较函数,并通过几种方式表示要排序的列:

  • 通过persons.sort(by('lastName', 'firstName'))
  • 通过选择器dates.sort(by(x => x.toISOString()))
  • 降序[3, 2, 4, 1].sort(by(desc(n => n)))[3, 2, 1, 0]
  • 忽略大小写['B', 'D', 'c', 'a'].sort(by(ignoreCase(x => x))).join('')'aBcD'

它与thenBy中提到的不错的this answer类似,但以下差异可能更符合某些人的口味:

  • 比面向对象的(请参见thenBy流利的API)更实用的方法
  • 有点语法,仍然可读性很强,就像SQL一样自然。
  • 完全在TypeScript中实现,以从类型安全性和类型表示性中受益。

答案 14 :(得分:0)

我自己的用于处理ES6可迭代对象的库(blinq)允许(除其他事项外)简单的多级排序

const blinq = window.blinq.blinq
// or import { blinq } from 'blinq'
// or const { blinq } = require('blinq')
const dates = [{
    day: 1, month: 10, year: 2000
  },
  {
    day: 1, month: 1, year: 2000
  },
  {
    day: 2, month: 1, year: 2000
  },
  {
    day: 1, month: 1, year: 1999
  },
  {
    day: 1, month: 1, year: 2000
  }
]
const sortedDates = blinq(dates)
  .orderBy(x => x.year)
  .thenBy(x => x.month)
  .thenBy(x => x.day);

console.log(sortedDates.toArray())
// or console.log([...sortedDates])
<script src="https://cdn.jsdelivr.net/npm/blinq@2.0.2"></script>

答案 15 :(得分:0)

来自GitHub

Cassandra