Javascript-根据另一个数组对数组进行排序


166

是否可以对看起来像这样的数组进行排序和重新排列:

itemsArray = [ 
    ['Anne', 'a'],
    ['Bob', 'b'],
    ['Henry', 'b'],
    ['Andrew', 'd'],
    ['Jason', 'c'],
    ['Thomas', 'b']
]

匹配此数组的安排:

sortingArr = [ 'b', 'c', 'b', 'b', 'a', 'd' ]

不幸的是,我没有任何要跟踪的ID。我将需要优先处理items-array,以使其尽可能接近sortingArr。

更新:

这是我正在寻找的输出:

itemsArray = [    
    ['Bob', 'b'],
    ['Jason', 'c'],
    ['Henry', 'b'],
    ['Thomas', 'b']
    ['Anne', 'a'],
    ['Andrew', 'd'],
]

知道如何做到这一点吗?


如果你不想手动做的一切,看看阵列功能罪PHP.js
2012年

仅通过遍历sortingArray并重写itemsArray
mplungjan '11

6
如果多个数组具有相同的排序值(即“ b”),您如何确定哪个项目在排序数组中的位置?对于'Bob','Henry'和'Thomas'都具有'b'的值-您如何确定哪一个排在第一,第三和第四呢?
米奇·斯奇韦尔

@MitchS是否可以优先安排从左到右的阅读?这真是令人头疼,因为我没有任何要比较的ID。
user1448892 2012年

从左到右是指它们在原始itemsArray中出现的顺序吗?
米奇·斯奇威尔

Answers:


74

就像是:

items = [ 
    ['Anne', 'a'],
    ['Bob', 'b'],
    ['Henry', 'b'],
    ['Andrew', 'd'],
    ['Jason', 'c'],
    ['Thomas', 'b']
]

sorting = [ 'b', 'c', 'b', 'b', 'c', 'd' ];
result = []

sorting.forEach(function(key) {
    var found = false;
    items = items.filter(function(item) {
        if(!found && item[1] == key) {
            result.push(item);
            found = true;
            return false;
        } else 
            return true;
    })
})

result.forEach(function(item) {
    document.writeln(item[0]) /// Bob Jason Henry Thomas Andrew
})

这是一个较短的代码,但是会破坏sorting数组:

result = items.map(function(item) {
    var n = sorting.indexOf(item[1]);
    sorting[n] = '';
    return [n, item]
}).sort().map(function(j) { return j[1] })

27
二次复杂度!尝试使用大量数据…
朱利安·罗耶

6
@ thg435:复杂度与“优化”无关,除非保证数据量很小(在这里可能就是这种情况)。
朱利安·罗耶

2
@georg关于作用于数据结构的算法的复杂性,对具有二次(或更糟)复杂性的算法进行优化永远不会过早,而且始终是必要的(除非您可以保证数据集的大小会很小) 。性能差异(从字面上看)以数量级表示。
Abion47 '19

236

一线答案。

itemsArray.sort(function(a, b){  
  return sortingArr.indexOf(a) - sortingArr.indexOf(b);
});

10
这将变异itemsArray。根据性能要求,这样做会更加安全itemsArray.slice().sort(...)
Sawtaytoes


8
它的确返回了数组,但它也进行了排序并变异了原始数组。
mynameistechno

2
这应该是真正的答案
urmurmur

6
@Morvael,这是由于此答案要求sortingArr包含中的所有值itemsArray。解决的方法是,如果项目不在以下位置,则将其推到阵列的后面sortingArrallProducts.sort((product1, product2) => { const index1 = manualSort.indexOf(product1.id); const index2 = manualSort.indexOf(product2.id); return ( (index1 > -1 ? index1 : Infinity) - (index2 > -1 ? index2 : Infinity) ); });
Freshollie

34

如果使用本机数组排序功能,则可以传入自定义比较器以对数组进行排序。如果第一个值小于第二个,比较器应返回一个负数;如果相等,则返回零;如果第一个值大于一个,则比较器应返回一个正数。

因此,如果我理解您给出的示例正确,则可以执行以下操作:

function sortFunc(a, b) {
  var sortingArr = [ 'b', 'c', 'b', 'b', 'c', 'd' ];
  return sortingArr.indexOf(a[1]) - sortingArr.indexOf(b[1]);
}

itemsArray.sort(sortFunc);

3
那将行不通,indexOf返回的第一个索引将是b,b,b,c,c,d 。
Mitch Satchwell

谢谢,但是我希望itemsArray的输出与sortingArray相匹配。
user1448892

6
如果中的“ id” sortingArr是唯一的,我更喜欢这个答案-幸运的是,在我的情况下,它们是:)
dillondrenzek

3
您应该声明sortingArray函数的外部,以便在每次排序迭代时重新声明它
aurumpotestasest

26

情况1:原始问题(无库)

很多其他可行的答案。:)

情况2:原始问题(Lodash.js或Underscore.js)

var groups = _.groupBy(itemArray, 1);
var result = _.map(sortArray, function (i) { return groups[i].shift(); });

情况3:对Array1进行排序,就好像它是Array2

我猜想大多数人来这里都是为了寻找与PHP的array_multisort相同的东西(我这样做了),所以我想我也应该发布这个答案。有两种选择:

1.有一个现有的JS实现array_multisort()。感谢@Adnan在评论中指出。不过,它很大。

2.写你自己的。(JSFiddle演示

function refSort (targetData, refData) {
  // Create an array of indices [0, 1, 2, ...N].
  var indices = Object.keys(refData);

  // Sort array of indices according to the reference data.
  indices.sort(function(indexA, indexB) {
    if (refData[indexA] < refData[indexB]) {
      return -1;
    } else if (refData[indexA] > refData[indexB]) {
      return 1;
    }
    return 0;
  });

  // Map array of indices to corresponding values of the target array.
  return indices.map(function(index) {
    return targetData[index];
  });
}

3. Lodash.jsUnderscore.js(既叫好,小库,集中表现)提供的辅助功能,让你这样做:

    var result = _.chain(sortArray)
      .pairs()
      .sortBy(1)
      .map(function (i) { return itemArray[i[0]]; })
      .value();

...将(1)将sortArray分组为[index, value]对,(2)按值对它们进行排序(您也可以在此处提供回调),(3)将每个对替换为itemArray中索引为对起源。


1
出色的解决方案,或者,如果您的数据结构稍微复杂一点,您也可以使用_.indexBy并删除该偏移量
Frozenfire'Frenchfire '16

20

这可能为时已晚,但是您也可以使用ES6样式的以下代码的某些修改版本。此代码适用于以下数组:

var arrayToBeSorted = [1,2,3,4,5];
var arrayWithReferenceOrder = [3,5,8,9];

实际操作:

arrayToBeSorted = arrayWithReferenceOrder.filter(v => arrayToBeSorted.includes(v));

ES5中的实际操作:

arrayToBeSorted = arrayWithReferenceOrder.filter(function(v) {
    return arrayToBeSorted.includes(v);
});

应该导致 arrayToBeSorted = [3,5]

不会破坏引用数组。


4
如果我的arrayToBeSorted是对象数组,即:{1:{…},2:{…},3:{…},4:{…},5:{…}},该怎么办?但是arrayWithReferenceOrder只是一个普通的数组?
水晶

3
@sushruth如何对数组排序?
hitautodestruct

@Crystal,这是一个对象,而不是对象数组。对象中的元素/项目没有顺序,即未设置其顺序。对象数组看起来像[{name: "1"}, {name: "2"}, {name: "3"}, ...]
JohnK

8

我将使用一个中间对象(itemsMap),从而避免二次复杂度:

function createItemsMap(itemsArray) { // {"a": ["Anne"], "b": ["Bob", "Henry"], …}
  var itemsMap = {};
  for (var i = 0, item; (item = itemsArray[i]); ++i) {
    (itemsMap[item[1]] || (itemsMap[item[1]] = [])).push(item[0]);
  }
  return itemsMap;
}

function sortByKeys(itemsArray, sortingArr) {
  var itemsMap = createItemsMap(itemsArray), result = [];
  for (var i = 0; i < sortingArr.length; ++i) {
    var key = sortingArr[i];
    result.push([itemsMap[key].shift(), key]);
  }
  return result;
}

参见http://jsfiddle.net/eUskE/


6
var sortedArray = [];
for(var i=0; i < sortingArr.length; i++) {
    var found = false;
    for(var j=0; j < itemsArray.length && !found; j++) {
        if(itemsArray[j][1] == sortingArr[i]) {
            sortedArray.push(itemsArray[j]);
            itemsArray.splice(j,1);
            found = true;
        }
    }
}

http://jsfiddle.net/s7b2P/

结果顺序:Bob,Jason,Henry,Thomas,Anne,Andrew


6

为什么不这样

//array1: array of elements to be sorted
//array2: array with the indexes

array1 = array2.map((object, i) => array1[object]);

地图功能可能并非在所有版本的 Javascript


1
这是最干净的解决方案,应该被接受。谢谢!
纽曼

3
let a = ['A', 'B', 'C' ]

let b = [3, 2, 1]

let c = [1.0, 5.0, 2.0]

// these array can be sorted by sorting order of b

const zip = rows => rows[0].map((_, c) => rows.map(row => row[c]))

const sortBy = (a, b, c) => {
  const zippedArray = zip([a, b, c])
  const sortedZipped = zippedArray.sort((x, y) => x[1] - y[1])

  return zip(sortedZipped)
}

sortBy(a, b, c)

2
请考虑添加简短的解释/说明,以解释此代码为何/如何回答问题。
Yannis,

3

这是我一直在寻找的内容,也是我根据另一个数组对数组进行排序的结果:

这是On ^ 3,可能不是最佳做法(ES6)

function sortArray(arr, arr1){
      return arr.map(item => {
        let a = [];
        for(let i=0; i< arr1.length; i++){
          for (const el of item) {
            if(el == arr1[i]){
              a.push(el);
            }   
            }
          }
          return a;
      });
    }
    
    const arr1 = ['fname', 'city', 'name'];
  const arr = [['fname', 'city', 'name'],
  ['fname', 'city', 'name', 'name', 'city','fname']];
  console.log(sortArray(arr,arr1));
可能对某人有帮助


2

我必须对从API接收到的JSON有效负载执行此操作,但是它的顺序不符合我想要的顺序。

数组是引用数组,您要的数组是第二个数组,排序依据:

var columns = [
    {last_name: "last_name"},
    {first_name: "first_name"},
    {book_description: "book_description"},
    {book_id: "book_id"},
    {book_number: "book_number"},
    {due_date: "due_date"},
    {loaned_out: "loaned_out"}
];

我将这些作为对象进行,因为它们最终将具有其他属性。

创建的数组:

 var referenceArray= [];
 for (var key in columns) {
     for (var j in columns[key]){
         referenceArray.push(j);
     }
  }

将此与数据库的结果集一起使用。我不知道它的效率如何,但是由于我使用的列数很少,所以效果很好。

result.forEach((element, index, array) => {                            
    var tr = document.createElement('tr');
    for (var i = 0; i < referenceArray.length - 1; i++) {
        var td = document.createElement('td');
        td.innerHTML = element[referenceArray[i]];
        tr.appendChild(td);

    }
    tableBody.appendChild(tr);
}); 

2

要获取新的有序数组,您可以采用Map并收集数组中具有所需键的所有项目,并通过获取所需组中经过筛选的元素来映射所需的有序键。

var itemsArray = [['Anne', 'a'], ['Bob', 'b'], ['Henry', 'b'], ['Andrew', 'd'], ['Jason', 'c'], ['Thomas', 'b']],
    sortingArr = [ 'b', 'c', 'b', 'b', 'a', 'd' ],
    map = itemsArray.reduce((m, a) => m.set(a[1], (m.get(a[1]) || []).concat([a])), new Map),
    result = sortingArr.map(k => (map.get(k) || []).shift());

console.log(result);


fa这是我的最爱,我做了同样的事情{}而不是使用Map🤷‍♂️
Can Rau

2
let sortedOrder = [ 'b', 'c', 'b', 'b' ]
let itemsArray = [ 
    ['Anne', 'a'],
    ['Bob', 'b'],
    ['Henry', 'b'],
    ['Andrew', 'd'],
    ['Jason', 'c'],
    ['Thomas', 'b']
]
a.itemsArray(function (a, b) {
    let A = a[1]
    let B = b[1]

    if(A != undefined)
        A = A.toLowerCase()

    if(B != undefined)
        B = B.toLowerCase()

    let indA = sortedOrder.indexOf(A)
    let indB = sortedOrder.indexOf(B)

    if (indA == -1 )
        indA = sortedOrder.length-1
    if( indB == -1)
        indB = sortedOrder.length-1

    if (indA < indB ) {
        return -1;
    } else if (indA > indB) {
        return 1;
    }
    return 0;
})

如果引用数组中没有排序键,则此解决方案将在对象末尾附加对象


0

这应该工作:

var i,search, itemsArraySorted = [];
while(sortingArr.length) {
    search = sortingArr.shift();
    for(i = 0; i<itemsArray.length; i++) {
        if(itemsArray[i][1] == search) {
            itemsArraySorted.push(itemsArray[i]);
            break;
        }
    } 
}

itemsArray = itemsArraySorted;

0

您可以尝试此方法。

const sortListByRanking = (rankingList, listToSort) => {
  let result = []

  for (let id of rankingList) {
    for (let item of listToSort) {
      if (item && item[1] === id) {
        result.push(item)
      }
    }
  }

  return result
}

0

ES6

const arrayMap = itemsArray.reduce(
  (accumulator, currentValue) => ({
    ...accumulator,
    [currentValue[1]]: currentValue,
  }),
  {}
);
const result = sortingArr.map(key => arrayMap[key]);

使用不同输入数组的更多示例


0

如果您到达这里需要使用对象数组来完成此操作,则可以使用@Durgpal Singh的出色答案:

const itemsArray = [
  { name: 'Anne', id: 'a' },
  { name: 'Bob', id: 'b' },
  { name: 'Henry', id: 'b' },
  { name: 'Andrew', id: 'd' },
  { name: 'Jason', id: 'c' },
  { name: 'Thomas', id: 'b' }
]

const sortingArr = [ 'b', 'c', 'b', 'b', 'a', 'd' ]

Object.keys(itemsArray).sort((a, b) => {
  return sortingArr.indexOf(itemsArray[a].id) - sortingArr.indexOf(itemsArray[b].id);
})

-1

使用jQuery中的$ .inArray()方法。然后,您可以做这样的事情

var sortingArr = [ 'b', 'c', 'b', 'b', 'c', 'd' ];
var newSortedArray = new Array();

for(var i=sortingArr.length; i--;) {
 var foundIn = $.inArray(sortingArr[i], itemsArray);
 newSortedArray.push(itemsArray[foundIn]);
}

-1

使用两个数组的交集。

例如:

var sortArray = ['a', 'b', 'c',  'd', 'e'];

var arrayToBeSort = ['z', 's', 'b',  'e', 'a'];

_.intersection(sortArray, arrayToBeSort) 

=> ['a','b','e']

如果'z和's'不在第一个数组的范围内,请在结果末尾附加


-4

您可以执行以下操作:

function getSorted(itemsArray , sortingArr ) {
  var result = [];
  for(var i=0; i<arr.length; i++) {
    result[i] = arr[sortArr[i]];
  }
  return result;
}

您可以在这里进行测试

注意:这假定您传入的数组大小相等,如果不是这种情况,则需要添加一些其他检查。

参考链接

参考


这非常需要编辑。jfiddle不仅返回错误的结果,而且函数参数名称与内部内容不匹配?
twobob
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.