So sánh mảng đối tượng JavaScript để có được tối thiểu / tối đa


95

Tôi có một mảng các đối tượng và tôi muốn so sánh các đối tượng đó trên một thuộc tính đối tượng cụ thể. Đây là mảng của tôi:

var myArray = [
    {"ID": 1, "Cost": 200},
    {"ID": 2, "Cost": 1000},
    {"ID": 3, "Cost": 50},
    {"ID": 4, "Cost": 500}
]

Tôi muốn xác định cụ thể "chi phí" và nhận giá trị tối thiểu và tối đa. Tôi nhận ra rằng tôi chỉ có thể lấy các giá trị chi phí và đẩy chúng vào một mảng javascript và sau đó chạy Fast JavaScript Max / Min .

Tuy nhiên, có cách nào dễ dàng hơn để làm điều này bằng cách bỏ qua bước mảng ở giữa và loại bỏ trực tiếp các thuộc tính đối tượng (trong trường hợp này là "Chi phí") không?

Câu trả lời:


53

Trong trường hợp này, cách nhanh nhất là lặp lại tất cả các phần tử và so sánh nó với giá trị cao nhất / thấp nhất cho đến nay.

(Tạo một mảng, gọi các phương thức mảng là quá mức cần thiết cho thao tác đơn giản này).

 // There's no real number bigger than plus Infinity
var lowest = Number.POSITIVE_INFINITY;
var highest = Number.NEGATIVE_INFINITY;
var tmp;
for (var i=myArray.length-1; i>=0; i--) {
    tmp = myArray[i].Cost;
    if (tmp < lowest) lowest = tmp;
    if (tmp > highest) highest = tmp;
}
console.log(highest, lowest);

Điều này có lý, tôi đã bị mắc kẹt với việc suy nghĩ về việc so sánh dữ liệu bên trong mảng với nhau thay vì một số cao / thấp bên ngoài.
firerawndagger

1
Điều duy nhất tôi sẽ thay đổi là đặt thấp nhất và cao nhất là hơi thừa. Tôi thà lặp lại một lần ít hơn và đặtlowest=highest=myArray[0] và sau đó bắt đầu vòng lặp tại 1.
J. Holmes

1
@ 32bitkid Điểm tốt. nên được myArray[0].Cost, mặc dù. Nhưng, nếu không có phần tử đầu tiên, một lỗi sẽ được đưa ra. Vì vậy, cần kiểm tra bổ sung, có thể hoàn tác việc tăng hiệu suất nhỏ.
Rob W

Tôi đến đây bởi vì tôi muốn bản thân đối tượng quay trở lại. Không phải là giá trị thấp nhất, điều này thật dễ dàng. Bất kỳ đề xuất?
Héo

1
@Wilt Có, hãy duy trì một biến khác được cập nhật khi bạn tìm thấy giá trị thấp nhất, tức là var lowestObject; for (...)if (tmp < lowest) { lowestObject = myArray[i]; lowest = tmp; }
Rob W

159

Việc rút gọn phù hợp với những thứ như thế này: để thực hiện các phép toán tổng hợp (như min, max, avg, v.v.) trên một mảng đối tượng và trả về một kết quả:

myArray.reduce(function(prev, curr) {
    return prev.Cost < curr.Cost ? prev : curr;
});

... hoặc bạn có thể xác định hàm bên trong đó bằng cú pháp hàm ES6:

(prev, curr) => prev.Cost < curr.Cost ? prev : curr

Nếu bạn muốn dễ thương, bạn có thể gắn cái này vào mảng:

Array.prototype.hasMin = function(attrib) {
    return (this.length && this.reduce(function(prev, curr){ 
        return prev[attrib] < curr[attrib] ? prev : curr; 
    })) || null;
 }

Bây giờ bạn chỉ có thể nói:

myArray.hasMin('ID')  // result:  {"ID": 1, "Cost": 200}
myArray.hasMin('Cost')    // result: {"ID": 3, "Cost": 50}
myEmptyArray.hasMin('ID')   // result: null

Xin lưu ý rằng nếu bạn có ý định sử dụng nó, nó không có đầy đủ kiểm tra cho mọi tình huống. Nếu bạn truyền vào một mảng các kiểu nguyên thủy, nó sẽ không thành công. Nếu bạn kiểm tra một thuộc tính không tồn tại hoặc nếu không phải tất cả các đối tượng đều chứa thuộc tính đó, bạn sẽ nhận được phần tử cuối cùng. Phiên bản này cồng kềnh hơn một chút, nhưng có những kiểm tra sau:

Array.prototype.hasMin = function(attrib) {
    const checker = (o, i) => typeof(o) === 'object' && o[i]
    return (this.length && this.reduce(function(prev, curr){
        const prevOk = checker(prev, attrib);
        const currOk = checker(curr, attrib);
        if (!prevOk && !currOk) return {};
        if (!prevOk) return curr;
        if (!currOk) return prev;
        return prev[attrib] < curr[attrib] ? prev : curr; 
    })) || null;
 }

13
Câu trả lời tốt nhất theo ý kiến ​​của tôi. Nó không thay đổi mảng và ngắn gọn hơn rất nhiều của nó so với các câu trả lời mà nói "Tạo một mảng, cách gọi phương pháp mảng là quá mức cần thiết cho hoạt động đơn giản này"
Julian Mann

Đây là câu trả lời tuyệt đối tốt nhất cho hiệu suất của các tập dữ liệu lớn (30+ cột / 100k hàng).
cerd

2
Chỉ tự hỏi, khi giảm kiểm tra phần tử đầu tiên của mảng sẽ không prev.Costđược xác định? Hay nó bắt đầu bằng 0?
GrayedFox

1
Điểm tốt @Saheb. Tôi vừa chỉnh sửa nên nó sẽ trả về null trong trường hợp đó.
Tristan Reid

1
Tôi cũng đã thêm một số kiểm tra cho các đầu vào khác có thể gây ra sự cố, như không phải đối tượng hoặc nếu thuộc tính đó bị thiếu trong một số đối tượng. Cuối cùng, nó trở nên hơi nặng tay đối với hầu hết các trường hợp, tôi nghĩ.
Tristan Reid

26

Sử dụng sort, nếu bạn không quan tâm đến mảng được sửa đổi.

myArray.sort(function (a, b) {
    return a.Cost - b.Cost
})

var min = myArray[0],
    max = myArray[myArray.length - 1]

3
phân loại đầy đủ không phải là cách nhanh nhất để tìm tối thiểu / tối đa nhưng tôi đoán nó sẽ hoạt động.
J. Holmes

4
Chỉ cần lưu ý rằng điều này sẽ sửa đổi myArray, điều này có thể không được mong đợi.
ziesemer

Sắp xếp một mảng chậm hơn việc duyệt qua nó. Sắp xếp độ phức tạp O(nlog(n))O(n)
:, duyệt

17

Sử dụng các Mathchức năng và rút ra các giá trị bạn muốnmap .

Đây là jsbin:

https://jsbin.com/necosu/1/edit?js,console

var myArray = [{
    "ID": 1,
    "Cost": 200
  }, {
    "ID": 2,
    "Cost": 1000
  }, {
    "ID": 3,
    "Cost": 50
  }, {
    "ID": 4,
    "Cost": 500
  }],

  min = Math.min.apply(null, myArray.map(function(item) {
    return item.Cost;
  })),
  max = Math.max.apply(null, myArray.map(function(item) {
    return item.Cost;
  }));

console.log('min', min);//50
console.log('max', max);//1000

CẬP NHẬT:

Nếu bạn muốn sử dụng ES6:

var min = Math.min.apply(null, myArray.map(item => item.Cost)),
    max = Math.max.apply(null, myArray.map(item => item.Cost));

14
Trong ES6 sử dụng Toán tử Spread, chúng tôi không cần nữa apply. Nói một cách đơn giản - Math.min(...myArray.map(o => o.Cost))để tìm mức tối thiểu và Math.max(...myArray.map(o => o.Cost))để tìm mức tối đa.
Nitin

13

Tôi nghĩ câu trả lời của Rob W thực sự là câu trả lời đúng (+1), nhưng nói cho vui: nếu bạn muốn trở nên "thông minh", bạn có thể làm điều gì đó như sau:

var myArray = 
[
    {"ID": 1, "Cost": 200},
    {"ID": 2, "Cost": 1000},
    {"ID": 3, "Cost": 50},
    {"ID": 4, "Cost": 500}
]

function finder(cmp, arr, attr) {
    var val = arr[0][attr];
    for(var i=1;i<arr.length;i++) {
        val = cmp(val, arr[i][attr])
    }
    return val;
}

alert(finder(Math.max, myArray, "Cost"));
alert(finder(Math.min, myArray, "Cost"));

hoặc nếu bạn có cấu trúc lồng nhau sâu sắc, bạn có thể có thêm một chút chức năng và thực hiện như sau:

var myArray = 
[
    {"ID": 1, "Cost": { "Wholesale":200, Retail: 250 }},
    {"ID": 2, "Cost": { "Wholesale":1000, Retail: 1010 }},
    {"ID": 3, "Cost": { "Wholesale":50, Retail: 300 }},
    {"ID": 4, "Cost": { "Wholesale":500, Retail: 1050 }}
]

function finder(cmp, arr, getter) {
    var val = getter(arr[0]);
    for(var i=1;i<arr.length;i++) {
        val = cmp(val, getter(arr[i]))
    }
    return val;
}

alert(finder(Math.max, myArray, function(x) { return x.Cost.Wholesale; }));
alert(finder(Math.min, myArray, function(x) { return x.Cost.Retail; }));

Chúng có thể dễ dàng được biến tấu thành các dạng hữu ích / cụ thể hơn.


4
Tôi đã đánh giá các giải pháp của chúng tôi: jsperf.com/comparison-of-numbers . Sau khi tối ưu hóa mã của bạn (xem điểm chuẩn), hiệu suất của cả hai phương pháp là tương tự nhau. Nếu không có tối ưu hóa, phương pháp của tôi nhanh hơn 14 lần.
Rob W

2
@RoBW oh Tôi hoàn toàn mong chờ phiên bản của bạn là cách nhanh hơn, tôi chỉ cung cấp một thực hiện kiến trúc thay thế. :)
J. Holmes

@ 32bitkid Tôi cũng mong đợi như vậy, nhưng thật bất ngờ, phương pháp này gần như nhanh như vậy (sau khi tối ưu hóa), như đã thấy trong trường hợp thử nghiệm 3 của điểm chuẩn.
Rob W

1
@RobW Tôi đồng ý, tôi không thể mong đợi điều đó. Tôi tò mò. :) Có nghĩa là bạn luôn nên làm điểm chuẩn hơn là giả định.
J. Holmes

@RobW Mặc dù vậy, tôi nghĩ rằng với nhiều kết quả trình duyệt hơn, việc triển khai của bạn sẽ liên tục đánh bại các phiên bản chưa được tối ưu hóa và tối ưu hóa.
J. Holmes

6

cho Max

Math.max.apply(Math, myArray.map(a => a.Cost));

trong phút

Math.min.apply(Math, myArray.map(a => a.Cost));

5

Thử ( alà mảng, flà trường để so sánh)

let max= (a,f)=> a.reduce((m,x)=> m[f]>x[f] ? m:x);
let min= (a,f)=> a.reduce((m,x)=> m[f]<x[f] ? m:x);


2
Thích câu trả lời này, rất nhỏ gọn và dễ sử dụng.
Dean

3

Sử dụng Array.prototype.reduce () , bạn có thể bổ sung các hàm so sánh để xác định mục tối thiểu, tối đa, v.v. trong một mảng.

var items = [
  { name : 'Apple',  count : 3  },
  { name : 'Banana', count : 10 },
  { name : 'Orange', count : 2  },
  { name : 'Mango',  count : 8  }
];

function findBy(arr, key, comparatorFn) {
  return arr.reduce(function(prev, curr, index, arr) { 
    return comparatorFn.call(arr, prev[key], curr[key]) ? prev : curr; 
  });
}

function minComp(prev, curr) {
  return prev < curr;
}

function maxComp(prev, curr) {
  return prev > curr;
}

document.body.innerHTML  = 'Min: ' + findBy(items, 'count', minComp).name + '<br />';
document.body.innerHTML += 'Max: ' + findBy(items, 'count', maxComp).name;


3

Sử dụng Math.minMath.max:

var myArray = [
    { id: 1, cost: 200},
    { id: 2, cost: 1000},
    { id: 3, cost: 50},
    { id: 4, cost: 500}
]


var min = Math.min(...myArray.map(item => item.cost));
var max = Math.max(...myArray.map(item => item.cost));

console.log("min: " + min);
console.log("max: " + max);


2

Đây là giải pháp tốt hơn

    var myArray = [
    {"ID": 1, "Cost": 200},
    {"ID": 2, "Cost": 1000},
    {"ID": 3, "Cost": 50},
    {"ID": 4, "Cost": 500}
    ]
    var lowestNumber = myArray[0].Cost;
    var highestNumber = myArray[0].Cost;

    myArray.forEach(function (keyValue, index, myArray) {
      if(index > 0) {
        if(keyValue.Cost < lowestNumber){
          lowestNumber = keyValue.Cost;
        }
        if(keyValue.Cost > highestNumber) {
          highestNumber = keyValue.Cost;
        }
      }
    });
    console.log('lowest number' , lowestNumber);
    console.log('highest Number' , highestNumber);

2

Thêm vào câu trả lời của Tristan Reid (+ sử dụng es6), bạn có thể tạo một hàm chấp nhận một lệnh gọi lại, hàm này sẽ chứa toán tử bạn muốn áp dụng cho prevcurr:

const compare = (arr, key, callback) => arr.reduce((prev, curr) =>
    (callback(prev[key], curr[key]) ? prev : curr), {})[key];

    // remove `[key]` to return the whole object

Sau đó, bạn có thể chỉ cần gọi nó bằng cách sử dụng:

const costMin = compare(myArray, 'Cost', (a, b) => a < b);
const costMax = compare(myArray, 'Cost', (a, b) => a > b);

2

Điều này có thể đạt được với lodash's minByvà các maxBychức năng.

Lodash's minBymaxBytài liệu

_.minBy(array, [iteratee=_.identity])

_.maxBy(array, [iteratee=_.identity])

Các phương thức này chấp nhận một vòng lặp được gọi cho mỗi phần tử trong mảng để tạo ra tiêu chí mà giá trị được xếp hạng. Vòng lặp được gọi với một đối số: (giá trị).

Giải pháp

var myArray = [
    {"ID": 1, "Cost": 200},
    {"ID": 2, "Cost": 1000},
    {"ID": 3, "Cost": 50},
    {"ID": 4, "Cost": 500}
]

const minimumCostItem = _.minBy(myArray, "Cost");

console.log("Minimum cost item: ", minimumCostItem);

// Getting the maximum using a functional iteratee
const maximumCostItem = _.maxBy(myArray, function(entry) {
  return entry["Cost"];
});

console.log("Maximum cost item: ", maximumCostItem);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>


0

chúng ta có thể giải quyết vấn đề bằng hai cách tiếp cận, cả hai phương pháp đã được giải thích ở trên nhưng kiểm tra hiệu suất bị thiếu nên việc hoàn thành

1, java-script nguyên bản cách
2, sắp xếp đối tượng đầu tiên sau đó dễ dàng nhận tối đa tối thiểu từ đối tượng được sắp xếp

tôi cũng kiểm tra hiệu suất của cả hai phương pháp kéo

bạn cũng có thể chạy và kiểm tra hiệu suất ... Chúc bạn viết mã vui vẻ (:

//first approach 

var myArray = [
    {"ID": 1, "Cost": 200},
    {"ID": 2, "Cost": 1000},
    {"ID": 3, "Cost": 50},
    {"ID": 4, "Cost": 500}
]

var t1 = performance.now();;

let max=Math.max.apply(Math, myArray.map(i=>i.Cost))

let min=Math.min.apply(Math, myArray.map(i=>i.Cost))

var t2   = performance.now();;

console.log("native fuction took " + (t2 - t1) + " milliseconds.");

console.log("max Val:"+max)
console.log("min Val:"+min)

//  Second approach:


function sortFunc (a, b) {
    return a.Cost - b.Cost
} 

var s1 = performance.now();;
sortedArray=myArray.sort(sortFunc)


var minBySortArray = sortedArray[0],
    maxBySortArray = sortedArray[myArray.length - 1]
    
var s2   = performance.now();;
 console.log("sort funciton took  " + (s2 - s1) + " milliseconds.");  
console.log("max ValBySortArray :"+max)
console.log("min Val BySortArray:"+min)


0

Đối với một giải pháp ngắn gọn, hiện đại, người ta có thể thực hiện một reducethao tác trên mảng, theo dõi các giá trị tối thiểu và lớn nhất hiện tại, do đó mảng chỉ được lặp lại một lần (là tối ưu).

let [min, max] = myArray.reduce(([prevMin,prevMax], {Cost})=>
   [Math.min(prevMin, Cost), Math.max(prevMax, Cost)], [Infinity, -Infinity]);

Bản giới thiệu:


-2

Một câu khác, tương tự như câu trả lời của Kennebec, nhưng tất cả đều nằm trong một dòng:

maxsort = myArray.slice(0).sort(function (a, b) { return b.ID - a.ID })[0].ID; 

-2

Bạn có thể sử dụng đối tượng Array tích hợp sẵn để sử dụng Math.max / Math.min thay thế:

var arr = [1,4,2,6,88,22,344];

var max = Math.max.apply(Math, arr);// return 344
var min = Math.min.apply(Math, arr);// return 1
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.