최소 / 최대를 얻기 위해 자바 스크립트 객체 배열 비교


95

객체 배열이 있고 특정 객체 속성에서 해당 객체를 비교하고 싶습니다. 내 배열은 다음과 같습니다.

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

구체적으로 "비용"에 초점을 맞추고 최소값과 최대 값을 얻고 싶습니다. 비용 값을 가져 와서 자바 스크립트 배열로 밀어 넣은 다음 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);

이것은 의미가 있습니다. 저는 외부의 높은 / 낮은 숫자 대신 배열 내부의 데이터를 서로 비교하는 것에 대해 고민했습니다.
firedrawndagger

1
내가 변경할 유일한 것은 가장 낮고 가장 높게 설정하는 것입니다. 차라리 루프를 한 적은 시간을 설정합니다 lowest=highest=myArray[0]다음과 1.에서 루프를 시작합니다
J. 홈즈

1
@ 32bitkid 좋은 지적입니다. myArray[0].Cost그래도 이어야합니다 . 그러나 첫 번째 요소가 없으면 오류가 발생합니다. 따라서 약간의 성능 향상을 취소 할 수있는 추가 검사가 필요합니다.
Rob W

나는 물체 자체를 반환하기를 원하기 때문에 여기에 왔습니다. 가장 낮은 값이 아니라 쉬웠습니다. 어떤 제안?
윌트

1
@Wilt 예, 즉 당신이 가장 낮은 값을 발견 한 경우 업데이트되는 또 다른 변수를 유지 var lowestObject; for (...)하고if (tmp < lowest) { lowestObject = myArray[i]; lowest = tmp; }
롭 W

159

감소는 다음과 같은 작업에 유용합니다. 객체 배열에 대해 집계 연산 (최소, 최대, 평균 등)을 수행하고 단일 결과를 반환합니다.

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

13
제 생각에는 최고의 답변입니다. 그것은 ", 배열 메소드를 호출하는 것은 간단한 작업에 잔인한 배열 만들기"를 배열 말한다 대답보다는 그것의 훨씬 더 간결을 수정하지 않습니다
줄리안 맨

이는 대규모 데이터 세트 (30+ 열 / 100,000 행)의 성능에 대한 절대적인 최선의 답변입니다.
cerd

2
축소 검사가 언제 배열의 첫 번째 요소 prev.Cost가 정의되지 않았 는지 궁금 하십니까? 아니면 0으로 시작합니까?
GrayedFox

1
좋은 지적 @Saheb. 이 경우 null을 반환하도록 방금 편집했습니다.
Tristan Reid

1
또한 비 객체와 같은 문제를 일으킬 수있는 다른 입력에 대한 몇 가지 검사를 추가했거나 해당 속성이 일부 객체에서 누락되었는지 여부를 확인했습니다. 궁극적으로 내가 생각하는 대부분의 경우에 대해 약간 무거운 손을 내밀고 있습니다.
Tristan Reid

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

17

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

14
Spread Operator를 사용하는 ES6에서는 더 이상 apply. Math.min(...myArray.map(o => o.Cost))최소값 Math.max(...myArray.map(o => o.Cost))을 찾고 최대 값을 찾기 위해 간단히 말 하십시오.
Nitin

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 배 더 빠릅니다.
Rob W

2
@RoBW 오, 나는 당신의 버전이 훨씬 더 빠를 것으로 기대할 것입니다. 저는 단지 대체 아키텍처 구현을 제공하고있었습니다. :)
J. Holmes

@ 32bitkid 동일하게 예상했지만 놀랍게도이 방법은 벤치 마크의 테스트 케이스 3에서 볼 수 있듯이 거의 (최적화 후) 빠릅니다.
Rob 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

Try ( 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
이 답변을 사랑하므로 작고 사용하기 쉽습니다.
Dean

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

Tristan Reid의 답변 (+ es6 사용)에 추가하면 콜백을 수락하는 함수를 만들 수 있습니다. 여기에는 prevand에 적용하려는 연산자가 포함됩니다 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 minBymaxBy기능 으로 달성 할 수 있습니다 .

Lodash minBymaxBy문서

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

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

이 메소드는 값의 순위를 매기는 기준을 생성하기 위해 배열의 각 요소에 대해 호출되는 iteratee를 허용합니다. iteratee는 하나의 인수 (값)로 호출됩니다.

해결책

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, 네이티브 자바 스크립트 방식
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

Kennebec의 답변과 비슷하지만 모두 한 줄에 있습니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.