Javascript - sắp xếp mảng dựa trên mảng khác


166

Có thể sắp xếp và sắp xếp lại một mảng trông như thế này không:

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

để phù hợp với sự sắp xếp của mảng này:

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

Thật không may, tôi không có bất kỳ ID nào để theo dõi. Tôi sẽ cần ưu tiên mảng mảng để khớp với sortingArr càng gần càng tốt.

Cập nhật:

Đây là đầu ra tôi đang tìm kiếm:

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

Bất kỳ ý tưởng làm thế nào điều này có thể được thực hiện?


Nếu bạn không muốn làm tất cả mọi thứ bằng tay, hãy nhìn vào tội lỗi chức năng mảng PHP.js .
Adi

Chỉ bằng cách lặp qua sắp xếpArray và viết lại các mụcArray
mplungjan

6
Trường hợp nhiều mảng có cùng giá trị sắp xếp (nghĩa là 'b'), làm thế nào để bạn quyết định mục nào sẽ đi đâu trong mảng được sắp xếp? Với 'Bob', 'Henry' và 'Thomas', tất cả đều có giá trị 'b' - làm thế nào để bạn quyết định cái nào đi trước, thứ ba và thứ tư?
Mitch Satchwell

@MitchS có thể ưu tiên đọc từ trái sang phải không? Đây là một vấn đề đau đầu thực sự, vì tôi không có bất kỳ ID nào để so sánh.
dùng1448892

Từ trái sang phải bạn có nghĩa là thứ tự chúng xuất hiện trong các mục gốcArray?
Mitch Satchwell

Câu trả lời:


74

Cái gì đó như:

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

Đây là một mã ngắn hơn, nhưng nó phá hủy sortingmảng:

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

27
Độ phức tạp bậc hai! Hãy thử nó với một lượng lớn dữ liệu
mai

6
@ Jun435: độ phức tạp ít liên quan đến "tối ưu hóa", trừ khi khối lượng dữ liệu được đảm bảo nhỏ (có thể là trường hợp ở đây).
Julien Royer

2
@georg Khi nói đến sự phức tạp của các thuật toán tác động lên cấu trúc dữ liệu, việc tối ưu hóa các thuật toán với độ phức tạp bậc hai (hoặc tệ hơn) là không bao giờ sớm và luôn luôn cần thiết (trừ khi bạn có thể đảm bảo kích thước của tập dữ liệu sẽ nhỏ) . Sự khác biệt trong hiệu suất là (hoàn toàn theo nghĩa đen) được thể hiện theo thứ tự độ lớn.
Abion47

236

Câu trả lời một dòng.

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

10
Điều này sẽ đột biến itemsArray. Tùy thuộc vào yêu cầu hiệu suất, nó sẽ an toàn hơn rất nhiều để làm itemsArray.slice().sort(...).
Sawtaytoes

1
phương thức sắp xếp trả về một mảng. xem developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/iêu
Durgpal Singh

8
Nó trả về mảng, nhưng nó cũng sắp xếp đúng chỗ và biến đổi bản gốc.
mynameistechno

2
đây phải là câu trả lời thực sự
urmurmur 17/07/19

6
@Morvael, điều này là do câu trả lời này yêu cầu sortingArrphải chứa tất cả các giá trị trong itemsArray. Cách khắc phục là đẩy các vật phẩm ra phía sau mảng nếu chúng không tồn tại trong sortingArr:allProducts.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

Nếu bạn sử dụng hàm sắp xếp mảng gốc, bạn có thể chuyển vào một bộ so sánh tùy chỉnh được sử dụng khi sắp xếp mảng. Bộ so sánh sẽ trả về số âm nếu giá trị đầu tiên nhỏ hơn giá trị thứ hai, bằng 0 nếu chúng bằng nhau và số dương nếu giá trị đầu tiên lớn hơn.

Vì vậy, nếu tôi hiểu ví dụ bạn đưa ra một cách chính xác, bạn có thể làm một cái gì đó như:

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
Điều đó sẽ không hoạt động, thứ tự kết quả sẽ là b, b, b, c, c, d khi indexOftrả về chỉ số đầu tiên.
Mitch Satchwell

Cảm ơn bạn, nhưng tôi muốn đầu ra của itemArray phù hợp với sortingArray.
dùng1448892

6
Tôi thích câu trả lời này nếu "id" trong sortingArrlà duy nhất - trong đó, may mắn thay, chúng nằm trong trường hợp của tôi :)
dillondrenzek

3
Bạn nên khai báo sortingArraybên ngoài hàm để avoir khai báo lại nó trên mỗi lần lặp sắp xếp
aurumpotestasest

26

Trường hợp 1: Câu hỏi gốc (Không có thư viện)

Rất nhiều câu trả lời khác mà làm việc. :)

Trường hợp 2: Câu hỏi gốc (Lodash.js hoặc Underscore.js)

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

Trường hợp 3: Sắp xếp Array1 như thể đó là Array2

Tôi đoán rằng hầu hết mọi người đến đây đang tìm kiếm một tương đương với mảng_multisort của PHP (tôi đã làm) vì vậy tôi nghĩ tôi cũng sẽ đăng câu trả lời đó. Có một vài lựa chọn:

1. Hiện có một triển khai JS của Array_multisort () . Cảm ơn @Adnan đã chỉ ra nó trong các bình luận. Nó là khá lớn, mặc dù.

2. Viết của riêng bạn. ( Bản demo của 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.js hoặc Underscore.js (cả thư viện phổ biến, nhỏ hơn tập trung vào hiệu suất) cung cấp các hàm trợ giúp cho phép bạn thực hiện việc này:

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

... Cái nào sẽ (1) nhóm sortArray thành [index, value]cặp, (2) sắp xếp chúng theo giá trị (bạn cũng có thể cung cấp một cuộc gọi lại ở đây), (3) thay thế từng cặp bằng vật phẩm từ itemArray tại chỉ mục cặp có nguồn gốc từ.


1
Giải pháp tuyệt vời, thay vào đó, bạn có thể sử dụng _.indexBy và xóa ca nếu cấu trúc dữ liệu của bạn phức tạp hơn một chút
Frozenfire

20

điều này có lẽ đã quá muộn nhưng, bạn cũng có thể sử dụng một số phiên bản sửa đổi của mã dưới đây theo kiểu ES6. Mã này dành cho các mảng như:

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

Các hoạt động thực tế:

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

Hoạt động thực tế trong ES5:

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

Nên kết quả trong arrayToBeSorted = [3,5]

Không phá hủy mảng tham chiếu.


4
Điều gì sẽ xảy ra nếu tôi mảngToBeSort là một mảng của các đối tượng, ví dụ: {1: {,}, 2: {,} nhưng mảngWithReferenceOrder chỉ là một mảng bình thường?
Pha lê

3
@sushruth làm thế nào để sắp xếp các mảng?
hitautodesturation

@Crystal, đó là một đối tượng, không phải là một mảng các đối tượng. Các phần tử / vật phẩm trong một đối tượng không có thứ tự, nghĩa là thứ tự của chúng không được đặt. Một mảng các đối tượng sẽ trông giống như [{name: "1"}, {name: "2"}, {name: "3"}, ...].
JohnK

8

Tôi sẽ sử dụng một đối tượng trung gian ( itemsMap), do đó tránh sự phức tạp bậc hai:

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

Xem 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/

Thứ tự kết quả: Bob, Jason, Henry, Thomas, Anne, Andrew


6

Tại sao không phải là một cái gì đó như

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

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

Chức năng bản đồ có thể không khả dụng trên tất cả các phiên bản của Javascript


1
Đây là giải pháp sạch nhất, và nên được chấp nhận câu trả lời. Cám ơn!
newman

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
Vui lòng xem xét thêm một lời giải thích / mô tả ngắn gọn giải thích lý do / cách mã này trả lời câu hỏi.
Yannis

3

Đây là những gì tôi đang tìm kiếm và tôi đã làm để sắp xếp một mảng Mảng dựa trên một mảng khác:

Đó là ngày ^ 3 và có thể không phải là cách thực hành tốt nhất (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));
Nó có thể giúp ai đó


2

Tôi đã phải làm điều này cho một tải trọng JSON mà tôi nhận được từ một API, nhưng nó không theo thứ tự tôi muốn.

Mảng là mảng tham chiếu, mảng bạn muốn mảng thứ hai được sắp xếp theo:

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"}
];

Tôi đã làm những điều này như là các đối tượng bởi vì chúng sẽ có các thuộc tính khác cuối cùng.

Tạo mảng:

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

Đã sử dụng điều này với tập kết quả từ cơ sở dữ liệu. Tôi không biết hiệu quả của nó như thế nào nhưng với số lượng cột tôi đã sử dụng, nó hoạt động tốt.

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

Để có được một mảng được đặt hàng mới, bạn có thể lấy Mapvà thu thập tất cả các mục với khóa mong muốn trong một mảng và ánh xạ các khóa được yêu cầu bằng cách lấy phần tử đã chọn của nhóm mong muốn.

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


At Đó là fav của tôi, tôi cũng sử dụng tương tự {}thay vì 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;
})

Giải pháp này sẽ nối các đối tượng ở cuối nếu khóa sắp xếp không có trong mảng tham chiếu


0

cái này sẽ hoạt động:

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

Bạn có thể thử phương pháp này.

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

Thêm ví dụ với các mảng đầu vào khác nhau


0

Trong trường hợp bạn đến đây cần phải làm điều này với một loạt các đối tượng, thì đây là bản phóng tác của câu trả lời tuyệt vời của @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

Sử dụng phương thức $ .inArray () từ jQuery. Sau đó bạn có thể làm một cái gì đó như thế này

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

Sử dụng giao điểm của hai mảng.

Ví dụ:

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

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

_.intersection(sortArray, arrayToBeSort) 

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

nếu 'z và' s 'nằm ngoài phạm vi của mảng đầu tiên, hãy thêm nó vào cuối kết quả


-4

Bạn có thể làm một cái gì đó như thế này:

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

Bạn có thể kiểm tra nó ở đây .

Lưu ý: điều này giả sử các mảng bạn vượt qua có kích thước tương đương, bạn cần thêm một số kiểm tra bổ sung nếu điều này có thể không đúng.

liên kết giới thiệu

tham khảo


Điều này rất cần chỉnh sửa. jfiddle không chỉ trả về kết quả sai, tên đối số hàm không khớp với nội dung bên trong?
Twobob
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.