Порівняйте масив об’єктів JavaScript, щоб отримати мінімальну / максимальну


95

У мене є масив об’єктів, і я хочу порівняти ці об’єкти за певною властивістю об’єкта. Ось мій масив:

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

Я б хотів конкретно обнулити "вартість" і отримати мінімальне та максимальне значення. Я усвідомлюю, що можу просто взяти значення вартості і виштовхнути їх у масив javascript, а потім запустити Fast JavaScript Max / Min .

Однак чи існує простіший спосіб зробити це, обійшовши крок масиву посередині і відключивши властивості об’єктів (в даному випадку "Вартість") безпосередньо?

Відповіді:


53

У цьому випадку найшвидший спосіб - це перебір усіх елементів і порівняння його з найвищим / найнижчим значенням на сьогодні.

(Створення масиву, виклик методів масиву є надмірним для цієї простої операції).

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

Це має сенс, я застряг у думках про порівняння даних всередині масиву між собою, а не зовнішнє високе / низьке число.
shotrawndagger

1
Єдине, що я б змінив, це встановлення найнижчого і найвищого - це трохи зайве. Я радше би повторити на один раз менше часу і встановити, lowest=highest=myArray[0]а потім розпочати цикл о 1.
Дж. Холмс

1
@ 32bitkid Хороший момент. має бути myArray[0].Cost, однак. Але якщо першого елемента немає, буде видано помилку. Отже, потрібна додаткова перевірка, можливо, скасування невеликого підвищення продуктивності.
Роб W

Я прийшов сюди, тому що хочу повернути сам предмет. Не найнижче значення, що було легко. Будь-які пропозиції?
Уілт

1
@Wilt Так, підтримуйте ще одну змінну, яка оновлюється, коли ви знайдете найнижче значення, тобто var lowestObject; for (...)іif (tmp < lowest) { lowestObject = myArray[i]; lowest = tmp; }
Роб W,

163

Зменшення добре для таких речей: для виконання сукупних операцій (наприклад, min, max, avg тощо) над масивом об’єктів і повернення одного результату:

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

... або ви можете визначити цю внутрішню функцію за допомогою синтаксису функції ES6:

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

Якщо ви хочете бути милою, ви можете додати це до масиву:

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

Тепер ви можете просто сказати:

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

Зверніть увагу, що якщо ви маєте намір використовувати це, воно не має повних перевірок для кожної ситуації. Якщо ви передасте масив примітивних типів, він не вдасться. Якщо ви перевірите наявність властивості, яка не існує, або якщо не всі об’єкти містять цю властивість, ви отримаєте останній елемент. Ця версія трохи громіздкіша, але має такі перевірки:

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

14
На мою думку, найкраща відповідь. Він не змінює масив і його набагато стисліше, ніж відповідь, у якій сказано: "Створення масиву, використання методів масиву є надмірним для цієї простої операції"
Джуліан Манн,

Це абсолютна найкраща відповідь для продуктивності великих наборів даних (30+ стовпців / 100 тис. Рядків).
сертифікат

2
Просто цікаво, коли при проведенні перевірок reduction перший елемент масиву не prev.Costбуде визначений? Або він ініціює як 0?
GrayedFox

1
Хороший момент @Saheb. Я щойно відредагував, тому в такому випадку він поверне нуль.
Трістан Рід

1
Я також додав деякі перевірки на інші вхідні дані, які можуть спричинити проблеми, такі як не-об'єкти, або якщо ця властивість відсутня в деяких об'єктах. Зрештою, я думаю, це стає трохи важким для більшості випадків.
Трістан Рід

26

Використовуйте sort, якщо вам байдуже, щоб масив змінювався.

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

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

3
повне сортування - не найшвидший спосіб знайти мінімум / максимум, але, мабуть, це спрацює.
J. Holmes

4
Тільки майте на увазі, що це змінить myArray, що може не очікуватися.
ziesemer

Сортування масиву відбувається повільніше, ніж обхід його. Складність сортування:, O(nlog(n))обхід масиву:O(n)
Mourad Qqch

18

Використовуйте Mathфункції та виривайте потрібні значення map.

Ось 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

ОНОВЛЕННЯ:

Якщо ви хочете використовувати ES6:

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

15
У ES6 за допомогою Spread Operator нам більше не потрібно apply. Просто скажіть - Math.min(...myArray.map(o => o.Cost))для пошуку мінімуму і Math.max(...myArray.map(o => o.Cost))для пошуку максимуму.
Нітін,

13

Я думаю , що відповідь Роб W справді правильна (+1), але просто для розваги: ​​якщо ви хочете бути «розумним», ви можете зробити щось подібне:

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

або якщо у вас була глибоко вкладена структура, ви можете отримати трохи більше функціональних можливостей і зробити наступне:

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

Їх можна легко перетворити на більш корисні / конкретні форми.


4
Я порівняв наші рішення: jsperf.com/comparison-of-numbers . Після оптимізації коду (див. Еталон) продуктивність обох методів є подібною. Без оптимізації мій метод в 14 разів швидший.
Роб W

2
@RoBW о, я б повністю очікував, що ваша версія буде набагато швидшою, я просто пропонував альтернативну архітектурну реалізацію. :)
J. Holmes

@ 32bitkid Я очікував того ж, але, на диво, метод майже такий же швидкий (після оптимізації), як це видно на тестовому прикладі 3 тесту.
Роб W

1
@RobW, згоден, я б цього не очікував. Я заінтригований. :) Має на меті показати, що ви завжди повинні тестувати, а не припускати.
J. Holmes

@RobW Щоб було зрозуміло, я думаю, що з більшою кількістю результатів браузера ваша реалізація буде постійно перевершувати неоптимізовану та оптимізовану версії.
J. Holmes

6

для макс

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

протягом хв

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

5

Спробуйте ( aце масив, fце поле для порівняння)

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
Полюбіть цю відповідь, таку компактну та просту у використанні.
Дін

3

Використовуючи Array.prototype.reduce () , ви можете підключити функції порівняння, щоб визначити мінімальний, максимальний та інший елемент у масиві.

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

Використання Math.minта Math.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

Це краще рішення

    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

Додавши до відповіді Трістана Рід (+ за допомогою es6), ви можете створити функцію, яка приймає зворотний виклик, яка міститиме оператора, до якого ви хочете застосувати до prevі curr:

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

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

Тоді ви можете просто зателефонувати за допомогою:

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

2

Цього можна досягти за допомогою функцій lodash minByі maxBy.

Лодаша minByта maxByдокументація

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

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

Ці методи приймають ітератор, який викликається для кожного елемента масиву, щоб сформувати критерій, за яким значення ранжирується. Ітератор викликається одним аргументом: (значення).

Рішення

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

ми можемо вирішити проблему двома підходами, обидва методи вже пояснено вище, але тест продуктивності відсутній, тому заповнення цього

1, власний Java-сценарій
2, спочатку сортуйте об'єкт, потім легко отримати мінімум максимуму з відсортованого obj

Я також перевіряю ефективність обох буксирних підходів

Ви також можете запустити і перевірити продуктивність ... Щасливого кодування (:

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

Для стислого сучасного рішення можна виконати reduceоперацію над масивом, відстежуючи поточні мінімальні та максимальні значення, тому масив повторюється лише один раз (що є оптимальним).

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

Демо:


-2

Ще одна, подібна до відповіді Кеннебека, але все в одному рядку:

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

-2

Ви можете використовувати вбудований об’єкт Array для використання Math.max / Math.min:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.