Localizando o valor máximo de um atributo em uma matriz de objetos

413

Estou procurando uma maneira muito rápida, limpa e eficiente de obter o valor máximo de "y" na seguinte fatia JSON:

[
  {
    "x": "8/11/2009",
    "y": 0.026572007
  },
  {
    "x": "8/12/2009",
    "y": 0.025057454
  },
  {
    "x": "8/13/2009",
    "y": 0.024530916
  },
  {
    "x": "8/14/2009",
    "y": 0.031004457
  }
]

Um loop for é a única maneira de fazer isso? Estou interessado em usar de alguma forma Math.max.

Rio
fonte
4
Como você retornaria o objeto e não apenas o valor mínimo de atratividade encontrado?
Mike Lyons
1
Para meu próprio benefício, executei alguns testes rápidos de desempenho nisso. jsperf.com/finding-the-max-value-an-array-of-objects
Andy Polhill
1
JSBin das soluções jsbin.com/pagamujuge/edit?html,js,console
Andy Polhill

Respostas:

741

Para encontrar o yvalor máximo dos objetos em array:

Math.max.apply(Math, array.map(function(o) { return o.y; }))
tobyodavies
fonte
47
Você poderia expandir esta resposta para mostrar como retornar o objeto em que o valor máximo foi encontrado? Isso seria muito útil, obrigado!
Mike Lyons
19
Aqui está o violino! Esperamos que isso ajude a alguém jsfiddle.net/45c5r246
mili
24
@MikeLyons se você ainda se preocupam sobre como obter o objeto real: jsfiddle.net/45c5r246/34
tobyodavies
11
Por favor, expanda sua resposta!
John William Domingo
12
FWIW, meu entendimento é que quando você chama apply em uma função, ela executa a função com um valor especificado para thise uma série de argumentos especificados como uma matriz. O truque é que aplicar transforma o array em uma série de argumentos de função reais. Portanto, neste caso, ele finalmente chama Math.max(0.0265, 0.0250, 0.024, 0.031)com thisa função executada Math. Não vejo por que Math, francamente, não acho que a função exija um válido this. Ah, e aqui está uma explicação adequada: stackoverflow.com/questions/21255138/...
Daniel C
263

Encontre o objeto cuja propriedade "Y" tenha o maior valor em uma matriz de objetos

Uma maneira seria usar Array reduzir ..

const max = data.reduce(function(prev, current) {
    return (prev.y > current.y) ? prev : current
}) //returns object

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce http://caniuse.com/#search=reduce (IE9 e acima)

Se você não precisa oferecer suporte ao IE (apenas Edge), ou pode usar um pré-compilador como o Babel, pode usar a sintaxe mais concisa.

const max = data.reduce((prev, current) => (prev.y > current.y) ? prev : current)
Andy Polhill
fonte
7
Essa é uma boa resposta, no entanto, você gostaria de passar um valor inicial ou receberia um erro caso a matriz de dados estivesse vazia. ou seja, para um índice de incremento automático de objetos. const max = data.reduce((prev, current) => (prev.y > current.y) ? prev : current, 1)
25417 Juliusonzalez
2
Você levanta um ponto bom, eu provavelmente escolheria nullsobre 1.
Andy Polhill
25
Observe que isso retorna o objeto que tinha o valor máximo e não o valor máximo do objeto. Isso pode ou não ser o que você deseja. No meu caso, era o que eu queria. 1
John
1
Boa resposta complementar!
Legends
Excelente resposta! No começo, eu hesitei devido à redução, mas precisamos iterar de qualquer maneira, então por que não?
shapiro yaacov
146

ES6 limpo e simples (Babel)

const maxValueOfY = Math.max(...arrayToSearchIn.map(o => o.y), 0);

O segundo parâmetro deve garantir um valor padrão se arrayToSearchInestiver vazio.

Vitaliy Kotov
fonte
8
Também é bom saber que ele retorna -Infinity(a truthy valor) para um array vazio
icl7126
1
Isso é suportado na maioria dos navegadores modernos sem o Babel agora.
Eugene Kulabuhov 23/02
20
como ele retorna -Infinitypara um array vazio, você pode passar um valor inicial Math.max(...state.allProjects.map(o => o.id), 1);
juliangonzalez
5
Esta deve ser a resposta aceita agora ... definitivamente a abordagem mais concisa.
Nickb
1
para lidar com maiúsculas e minúsculas, mude 0para arrayToSearchIn[0].y. Tempo complexidade comparação: stackoverflow.com/a/53654364/860099
Kamil Kiełczewski
41

Comparação da árvore ONELINERS que lida com maiúsculas e minúsculas números (entrada na amatriz):

var maxA = a.reduce((a,b)=>a.y>b.y?a:b).y;  // 30 chars time complexity:  O(n)

var maxB = a.sort((a,b)=>b.y-a.y)[0].y;     // 27 chars time complexity:  O(nlogn)

var maxC = Math.max(...a.map(o=>o.y));      // 26 chars time complexity: >O(2n)

exemplo editável aqui . Ideias de: maxA , maxB e maxC (o efeito colateral de maxB é que a matriz aé alterada porque sortestá no local).

Para matrizes maiores, a Math.max...exceção será lançada : tamanho máximo da pilha de chamadas excedido (Chrome 76.0.3809, Safari 12.1.2, data 2019-09-13)

Kamil Kiełczewski
fonte
2
Métodos muito inteligentes para realizar a tarefa. Nice
TetraDev 7/08/19
Desculpe, votado por engano e não foi possível desfazer sem editar sua pergunta, porque passou muito tempo.
Günter Zöchbauer 5/09/19
Graças à ótima análise.
d337 17/03
obrigado por esse detalhamento das opções disponíveis e pelas diferenças entre as abordagens.
FistOfFury
Uma coisa a destacar é que a opção B facilita muito a obtenção de todo o objeto com o yvalor máximo , deixando de fora .yo final.
FistOfFury
23

Bem, primeiro você deve analisar a string JSON, para poder acessar facilmente seus membros:

var arr = $.parseJSON(str);

Use o mapmétodo para extrair os valores:

arr = $.map(arr, function(o){ return o.y; });

Então você pode usar a matriz no maxmétodo:

var highest = Math.max.apply(this,arr);

Ou como uma linha:

var highest = Math.max.apply(this,$.map($.parseJSON(str), function(o){ return o.y; }));
Guffa
fonte
15
Não está etiquetado comjQuery
Robin van Baalen
1
@RobinvanBaalen: Sim, você está certo. No entanto, ele é marcado com JSON, mas a resposta aceita ignora isso, e tobyodavies também removeu que desde o assunto da pergunta ... Talvez eu devesse adicionar jquery para a questão ...;)
Guffa
8
Não importa muito se @tobyodavies ignorou o fato de que ele foi marcado json- ele não está usando uma biblioteca de javascript externo em sua resposta :)
Robin van Baalen
23

Gostaria de explicar a resposta concisa concisa passo a passo:

var objects = [{ x: 3 }, { x: 1 }, { x: 2 }];

// array.map lets you extract an array of attribute values
var xValues = objects.map(function(o) { return o.x; });
// es6
xValues = Array.from(objects, o => o.x);

// function.apply lets you expand an array argument as individual arguments
// So the following is equivalent to Math.max(3, 1, 2)
// The first argument is "this" but since Math.max doesn't need it, null is fine
var xMax = Math.max.apply(null, xValues);
// es6
xMax = Math.max(...xValues);

// Finally, to find the object that has the maximum x value (note that result is array):
var maxXObjects = objects.filter(function(o) { return o.x === xMax; });

// Altogether
xMax = Math.max.apply(null, objects.map(function(o) { return o.x; }));
var maxXObject = objects.filter(function(o) { return o.x === xMax; })[0];
// es6
xMax = Math.max(...Array.from(objects, o => o.x));
maxXObject = objects.find(o => o.x === xMax);


document.write('<p>objects: ' + JSON.stringify(objects) + '</p>');
document.write('<p>xValues: ' + JSON.stringify(xValues) + '</p>');
document.write('<p>xMax: ' + JSON.stringify(xMax) + '</p>');
document.write('<p>maxXObjects: ' + JSON.stringify(maxXObjects) + '</p>');
document.write('<p>maxXObject: ' + JSON.stringify(maxXObject) + '</p>');

Outras informações:

congusbongus
fonte
Ótima explicação! Pode ser um pouco mais fácil de ler se não estiver nos comentários do código, mas ainda assim - ótimo trabalho
Martin
12
var data = [
  { 'name': 'Vins', 'age': 27 },
  { 'name': 'Jan', 'age': 38 },
  { 'name': 'Alex', 'age': 80 },
  { 'name': 'Carl', 'age': 25 },
  { 'name': 'Digi', 'age': 40 }
];
var max = data.reduce(function (prev, current) {
   return (prev.age > current.age) ? prev : current
});
//output = {'name': 'Alex', 'age': 80}
Vin S
fonte
2
Como isso difere da resposta de @ AndyPolhill?
Lewis
7

se você (ou alguém aqui) estiver livre para usar a lodashbiblioteca de utilitários, ela possui uma função maxBy que seria muito útil no seu caso.

portanto, você pode usar como tal:

_.maxBy(jsonSlice, 'y');
kmonsoor
fonte
6

Ou um tipo simples! Mantendo a realidade :)

array.sort((a,b)=>a.y<b.y)[0].y
Ooki Koi
fonte
Boa ideia +1 (código mais curto), mas há um pequeno erro - mude a.y<a.ypara b.y-a.y. Comparação da complexidade de tempo aqui: stackoverflow.com/a/53654364/860099
Kamil Kiełczewski
2
Encontrar o máximo é O (n). Este é O (nlogn). Escrever código simples é bom, desde que a eficiência não seja sacrificada.
Wildhammer 14/02
@Wildhammer - na verdade, a micro-otimização vale a pena quando você tem evidências de que está otimizando um gargalo. . Na maioria dos casos, o código simples é a melhor escolha que o código de alta eficiência.
Kamil Kiełczewski
@ KamilKiełczewski Ambas as comparações de array nesse artigo têm a mesma complexidade de tempo, a diferença está em seu coeficiente. Por exemplo, um leva n unidades de tempo para encontrar a solução enquanto o outro é 7n. Na teoria da complexidade temporal, ambos são O (n). O que estamos falando no problema de encontrar max é a comparação de O (n) com O (n logn). Agora, se você pode garantir que n não excede 10, pode usar sua solução, caso contrário, o algoritmo O (n) é sempre o vencedor e o desempenho (experiência do usuário) é sempre anterior à experiência do desenvolvedor (pergunte ao pessoal da indústria, eles dizem isso!) .
Wildhammer 01/04
@ Wildhammer não - mesmo que sua matriz tenha n = 10000 elementos, o usuário não verá diferenças - aqui . A otimização de desempenho é boa apenas para gargalos de aplicativos (por exemplo, você precisa processar matrizes grandes) - mas na maioria dos casos, o foco no desempenho é uma abordagem incorreta e uma perda de tempo (= dinheiro). Este é um erro bem conhecido de abordagem de código - leia mais: "micro-otimização"
Kamil Kiełczewski
3

Cada matriz e obtenha o valor máximo com Math.

data.reduce((max, b) => Math.max(max, b.costo), data[0].costo);
Diego Santa Cruz Mendezú
fonte
2

Aqui está a solução mais curta (One Liner) ES6 :

Math.max(...values.map(o => o.y));
Subodh Singh
fonte
1
var max = 0;                
jQuery.map(arr, function (obj) {
  if (obj.attr > max)
    max = obj.attr;
});
Mephisto07
fonte
1
Here is very simple way to go:

Your DataSet.

let numberArray = [
  {
    "x": "8/11/2009",
    "y": 0.026572007
  },
  {
    "x": "8/12/2009",
    "y": 0.025057454
  },
  {
    "x": "8/13/2009",
    "y": 0.024530916
  },
  {
    "x": "8/14/2009",
    "y": 0.031004457
  }
]

1. First create Array, containing all the value of Y
let result = numberArray.map((y) => y)
console.log(result) >> [0.026572007,0.025057454,0.024530916,0.031004457]

2. let maxValue = Math.max.apply(null, result)
console.log(maxvalue) >> 0.031004457
Pushp Singh
fonte