Estou procurando uma maneira elegante de determinar qual elemento tem a maior ocorrência ( modo ) em uma matriz JavaScript.
Por exemplo, em
['pear', 'apple', 'orange', 'apple']
o 'apple'
elemento é o mais frequente.
javascript
mode
torno
fonte
fonte
Respostas:
Este é apenas o modo. Esta é uma
solução rápida e não otimizada. Deve ser O (n).function mode(array) { if(array.length == 0) return null; var modeMap = {}; var maxEl = array[0], maxCount = 1; for(var i = 0; i < array.length; i++) { var el = array[i]; if(modeMap[el] == null) modeMap[el] = 1; else modeMap[el]++; if(modeMap[el] > maxCount) { maxEl = el; maxCount = modeMap[el]; } } return maxEl; }
fonte
Houve alguns desenvolvimentos em javascript desde 2009 - pensei em adicionar outra opção. Estou menos preocupado com a eficiência até que seja realmente um problema, então minha definição de código "elegante" (conforme estipulado pelo OP) favorece a legibilidade - o que é obviamente subjetivo ...
function mode(arr){ return arr.sort((a,b) => arr.filter(v => v===a).length - arr.filter(v => v===b).length ).pop(); } mode(['pear', 'apple', 'orange', 'apple']); // apple
Neste exemplo específico, se dois ou mais elementos do conjunto tiverem ocorrências iguais, aquele que aparecer por último na matriz será retornado. Também vale a pena ressaltar que ele modificará seu array original - o que pode ser evitado se você desejar com uma
Array.slice
chamada prévia.Edit: atualizou o exemplo com algumas setas grossas ES6 porque 2015 aconteceu e eu acho que elas estão bonitas ... Se você está preocupado com a compatibilidade com versões anteriores, você pode encontrar isso no histórico de revisão .
fonte
De acordo com o
George Jempty's
pedido para ter o algoritmo responsável pelos empates, proponho uma versão modificada doMatthew Flaschen's
algoritmo.function modeString(array) { if (array.length == 0) return null; var modeMap = {}, maxEl = array[0], maxCount = 1; for (var i = 0; i < array.length; i++) { var el = array[i]; if (modeMap[el] == null) modeMap[el] = 1; else modeMap[el]++; if (modeMap[el] > maxCount) { maxEl = el; maxCount = modeMap[el]; } else if (modeMap[el] == maxCount) { maxEl += "&" + el; maxCount = modeMap[el]; } } return maxEl; }
Isso agora retornará uma string com o (s) elemento (s) de modo delimitado (s) por um
&
símbolo. Quando o resultado é recebido, ele pode ser dividido naquele&
elemento e você tem seu (s) modo (s).Outra opção seria retornar uma matriz de elemento (s) de modo como:
function modeArray(array) { if (array.length == 0) return null; var modeMap = {}, maxCount = 1, modes = []; for (var i = 0; i < array.length; i++) { var el = array[i]; if (modeMap[el] == null) modeMap[el] = 1; else modeMap[el]++; if (modeMap[el] > maxCount) { modes = [el]; maxCount = modeMap[el]; } else if (modeMap[el] == maxCount) { modes.push(el); maxCount = modeMap[el]; } } return modes; }
No exemplo acima, você seria capaz de manipular o resultado da função como um array de modos.
fonte
modes
a[array[0]]
como valor inicial. Isso garantirá que você tenha duplicatas emmodes
. Isso deve funcionarvar modes = []
==
para===
impor igualdade estritaCom base na resposta ES6 + do Emissário , você poderia usar
Array.prototype.reduce
para fazer sua comparação (ao invés de classificar, estalar e potencialmente transformar seu array), o que eu acho que parece bastante inteligente.const mode = (myArray) => myArray.reduce( (a,b,i,arr)=> (arr.filter(v=>v===a).length>=arr.filter(v=>v===b).length?a:b), null)
Estou padronizando para null, o que nem sempre fornecerá uma resposta verdadeira se null for uma opção possível pela qual você está filtrando, talvez esse possa ser um segundo argumento opcional
A desvantagem, como com várias outras soluções, é que ele não lida com 'estados de desenho', mas isso ainda poderia ser alcançado com uma função de redução um pouco mais envolvida.
fonte
a=['pear', 'apple', 'orange', 'apple']; b={}; max='', maxi=0; for(let k of a) { if(b[k]) b[k]++; else b[k]=1; if(maxi < b[k]) { max=k; maxi=b[k] } }
fonte
Como estou usando essa função como um questionário para os entrevistadores, posto minha solução:
const highest = arr => (arr || []).reduce( ( acc, el ) => { acc.k[el] = acc.k[el] ? acc.k[el] + 1 : 1 acc.max = acc.max ? acc.max < acc.k[el] ? el : acc.max : el return acc }, { k:{} }).max const test = [0,1,2,3,4,2,3,1,0,3,2,2,2,3,3,2] console.log(highest(test))
fonte
Tentando uma abordagem declarativa aqui. Essa solução cria um objeto para registrar as ocorrências de cada palavra. Em seguida, filtra o objeto em uma matriz, comparando o total de ocorrências de cada palavra com o valor mais alto encontrado no objeto.
const arr = ['hello', 'world', 'hello', 'again']; const tally = (acc, x) => { if (! acc[x]) { acc[x] = 1; return acc; } acc[x] += 1; return acc; }; const totals = arr.reduce(tally, {}); const keys = Object.keys(totals); const values = keys.map(x => totals[x]); const results = keys.filter(x => totals[x] === Math.max(...values));
fonte
Hora de outra solução:
function getMaxOccurrence(arr) { var o = {}, maxCount = 0, maxValue, m; for (var i=0, iLen=arr.length; i<iLen; i++) { m = arr[i]; if (!o.hasOwnProperty(m)) { o[m] = 0; } ++o[m]; if (o[m] > maxCount) { maxCount = o[m]; maxValue = m; } } return maxValue; }
Se a brevidade for importante (não), então:
function getMaxOccurrence(a) { var o = {}, mC = 0, mV, m; for (var i=0, iL=a.length; i<iL; i++) { m = a[i]; o.hasOwnProperty(m)? ++o[m] : o[m] = 1; if (o[m] > mC) mC = o[m], mV = m; } return mV; }
Se membros inexistentes devem ser evitados (por exemplo, matriz esparsa), um teste hasOwnProperty adicional é necessário:
function getMaxOccurrence(a) { var o = {}, mC = 0, mV, m; for (var i=0, iL=a.length; i<iL; i++) { if (a.hasOwnProperty(i)) { m = a[i]; o.hasOwnProperty(m)? ++o[m] : o[m] = 1; if (o[m] > mC) mC = o[m], mV = m; } } return mV; } getMaxOccurrence([,,,,,1,1]); // 1
Outras respostas aqui retornarão indefinidas .
fonte
Outra solução JS de: https://www.w3resource.com/javascript-exercises/javascript-array-exercise-8.php
Também pode tentar:
let arr =['pear', 'apple', 'orange', 'apple']; function findMostFrequent(arr) { let mf = 1; let m = 0; let item; for (let i = 0; i < arr.length; i++) { for (let j = i; j < arr.length; j++) { if (arr[i] == arr[j]) { m++; if (m > mf) { mf = m; item = arr[i]; } } } m = 0; } return item; } findMostFrequent(arr); // apple
fonte
Aqui está outra maneira ES6 de fazer isso com complexidade O (n)
const result = Object.entries( ['pear', 'apple', 'orange', 'apple'].reduce((previous, current) => { if (previous[current] === undefined) previous[current] = 1; else previous[current]++; return previous; }, {})).reduce((previous, current) => (current[1] >= previous[1] ? current : previous))[0]; console.log("Max value : " + result);
fonte
function mode(arr){ return arr.reduce(function(counts,key){ var curCount = (counts[key+''] || 0) + 1; counts[key+''] = curCount; if (curCount > counts.max) { counts.max = curCount; counts.mode = key; } return counts; }, {max:0, mode: null}).mode }
fonte
Aqui está minha solução para este problema, mas com números e usando o novo recurso 'Definir'. Não tem muito desempenho, mas definitivamente me diverti muito escrevendo isso e ele suporta vários valores máximos.
const mode = (arr) => [...new Set(arr)] .map((value) => [value, arr.filter((v) => v === value).length]) .sort((a,b) => a[1]-b[1]) .reverse() .filter((value, i, a) => a.indexOf(value) === i) .filter((v, i, a) => v[1] === a[0][1]) .map((v) => v[0]) mode([1,2,3,3]) // [3] mode([1,1,1,1,2,2,2,2,3,3,3]) // [1,2]
A propósito, não use isso para produção, esta é apenas uma ilustração de como você pode resolver isso apenas com as funções ES6 e Array.
fonte
Aqui está minha solução: -
function frequent(number){ var count = 0; var sortedNumber = number.sort(); var start = number[0], item; for(var i = 0 ; i < sortedNumber.length; i++){ if(start === sortedNumber[i] || sortedNumber[i] === sortedNumber[i+1]){ item = sortedNumber[i] } } return item } console.log( frequent(['pear', 'apple', 'orange', 'apple']))
fonte
Por uma questão de código realmente fácil de ler e sustentável, compartilho isto:
function getMaxOcurrences(arr = []) { let item = arr[0]; let ocurrencesMap = {}; for (let i in arr) { const current = arr[i]; if (ocurrencesMap[current]) ocurrencesMap[current]++; else ocurrencesMap[current] = 1; if (ocurrencesMap[item] < ocurrencesMap[current]) item = current; } return { item: item, ocurrences: ocurrencesMap[item] }; }
Espero que ajude alguém;)!
fonte
Esta solução pode retornar vários elementos de uma matriz em caso de empate. Por exemplo, um array
arr = [ 3, 4, 3, 6, 4, ];
tem dois valores de modo:
3
e6
.Aqui está a solução.
function find_mode(arr) { var max = 0; var maxarr = []; var counter = []; var maxarr = []; arr.forEach(function(){ counter.push(0); }); for(var i = 0;i<arr.length;i++){ for(var j=0;j<arr.length;j++){ if(arr[i]==arr[j])counter[i]++; } } max=this.arrayMax(counter); for(var i = 0;i<arr.length;i++){ if(counter[i]==max)maxarr.push(arr[i]); } var unique = maxarr.filter( this.onlyUnique ); return unique; }; function arrayMax(arr) { var len = arr.length, max = -Infinity; while (len--) { if (arr[len] > max) { max = arr[len]; } } return max; }; function onlyUnique(value, index, self) { return self.indexOf(value) === index; }
fonte
var mode = 0; var c = 0; var num = new Array(); var value = 0; var greatest = 0; var ct = 0;
Nota: ct é o comprimento da matriz.
function getMode() { for (var i = 0; i < ct; i++) { value = num[i]; if (i != ct) { while (value == num[i + 1]) { c = c + 1; i = i + 1; } } if (c > greatest) { greatest = c; mode = value; } c = 0; } }
fonte
const mode = (str) => { return str .split(' ') .reduce((data, key) => { let counter = data.map[key] + 1 || 1 data.map[key] = counter if (counter > data.counter) { data.counter = counter data.mode = key } return data }, { counter: 0, mode: null, map: {} }) .mode } console.log(mode('the t-rex is the greatest of them all'))
fonte
function mode(array){ var set = Array.from(new Set(array)); var counts = set.map(a=>array.filter(b=>b==a).length); var indices = counts.map((a,b)=>Math.max(...counts)===a?b:0).filter(b=>b!==0); var mode = indices.map(a=>set[a]); return mode; }
fonte
Experimente também, isso não leva em conta a versão do navegador.
function mode(arr){ var a = [],b = 0,occurrence; for(var i = 0; i < arr.length;i++){ if(a[arr[i]] != undefined){ a[arr[i]]++; }else{ a[arr[i]] = 1; } } for(var key in a){ if(a[key] > b){ b = a[key]; occurrence = key; } } return occurrence; } alert(mode(['segunda','terça','terca','segunda','terça','segunda']));
fonte
// O(n) var arr = [1, 2, 3, 2, 3, 3, 5, 6]; var duplicates = {}; max = ''; maxi = 0; arr.forEach((el) => { duplicates[el] = duplicates[el] + 1 || 1; if (maxi < duplicates[el]) { max = el; maxi = duplicates[el]; } }); console.log(max);
fonte
Esta é a versão moderna usando mapas integrados (portanto, funciona em mais do que coisas que podem ser convertidas em strings exclusivas):
'use strict'; const histogram = iterable => { const result = new Map(); for (const x of iterable) { result.set(x, (result.get(x) || 0) + 1); } return result; }; const mostCommon = iterable => { let maxCount = 0; let maxKey; for (const [key, count] of histogram(iterable)) { if (count > maxCount) { maxCount = count; maxKey = key; } } return maxKey; }; console.log(mostCommon(['pear', 'apple', 'orange', 'apple']));
fonte
Eu acho que você tem duas abordagens. Ambos têm vantagens.
Classifique e conte ou faça um loop e use uma tabela hash para fazer a contagem para você.
A tabela de hash é boa porque, depois de concluir o processamento, você também terá todos os elementos distintos. No entanto, se você tivesse milhões de itens, a tabela hash poderia acabar usando muita memória se a taxa de duplicação for baixa. A abordagem de classificação e contagem teria uma pegada de memória muito mais controlável.
fonte
var array = [1, 3, 6, 6, 6, 6, 7, 7, 12, 12, 17], c = {}, // counters s = []; // sortable array for (var i=0; i<array.length; i++) { c[array[i]] = c[array[i]] || 0; // initialize c[array[i]]++; } // count occurrences for (var key in c) { s.push([key, c[key]]) } // build sortable array from counters s.sort(function(a, b) {return b[1]-a[1];}); var firstMode = s[0][0]; console.log(firstMode);
fonte
Você pode tentar isto:
// using splice() // get the element with the highest occurence in an array function mc(a) { var us = [], l; // find all the unique elements in the array a.forEach(function (v) { if (us.indexOf(v) === -1) { us.push(v); } }); l = us.length; while (true) { for (var i = 0; i < l; i ++) { if (a.indexOf(us[i]) === -1) { continue; } else if (a.indexOf(us[i]) != -1 && a.length > 1) { // just delete it once at a time a.splice(a.indexOf(us[i]), 1); } else { // default to last one return a[0]; } } } } // using string.match method function su(a) { var s = a.join(), uelms = [], r = {}, l, i, m; a.forEach(function (v) { if (uelms.indexOf(v) === -1) { uelms.push(v); } }); l = uelms.length; // use match to calculate occurance times for (i = 0; i < l; i ++) { r[uelms[i]] = s.match(new RegExp(uelms[i], 'g')).length; } m = uelms[0]; for (var p in r) { if (r[p] > r[m]) { m = p; } else { continue; } } return m; }
fonte
Você poderia resolvê-lo em complexidade O (n)
var arr = [1,3,54,56,6,6,1,6]; var obj = {}; /* first convert the array in to object with unique elements and number of times each element is repeated */ for(var i = 0; i < arr.length; i++) { var x = arr[i]; if(!obj[x]) obj[x] = 1; else obj[x]++; } console.log(obj);//just for reference /* now traverse the object to get the element */ var index = 0; var max = 0; for(var obIndex in obj) { if(obj[obIndex] > max) { max = obj[obIndex]; index = obIndex; } } console.log(index+" got maximum time repeated, with "+ max +" times" );
Basta copiar e colar no console do Chrome para executar o código acima.
fonte
Esta função é uma função genérica para todo tipo de informação. Ele conta a ocorrência dos elementos e retorna a matriz com o máximo de elementos ocorridos.
function mode () { var arr = [].slice.call(arguments); if ((args.length == 1) && (typeof args[0] === "object")) { args = args[0].mode(); } var obj = {}; for(var i = 0; i < arr.length; i++) { if(obj[arr[i]] === undefined) obj[arr[i]] = 1; else obj[arr[i]]++; } var max = 0; for (w in obj) { if (obj[w] > max) max = obj[w]; } ret_val = []; for (w in obj) { if (obj[w] == max) ret_val.push(w); } return ret_val; }
fonte
function mode(){ var input = $("input").val().split(","); var mode = []; var m = []; var p = []; for(var x = 0;x< input.length;x++){ if(m.indexOf(input[x])==-1){ m[m.length]=input[x]; }} for(var x = 0; x< m.length;x++){ p[x]=0; for(var y = 0; y<input.length;y++){ if(input[y]==m[x]){ p[x]++; }}} for(var x = 0;x< p.length;x++){ if(p[x] ==(Math.max.apply(null, p))){ mode.push(m[x]); }} $("#output").text(mode);}
fonte
Aqui está o meu caminho. Tento agrupar o punho de dados.
const _ = require("underscore") var test = [ 1, 1, 2, 1 ]; var groupResult = _.groupBy(test, (e)=> e);
O groupResult deve ser
{ 1: [1, 1, 1] 2: [2] }
Em seguida, encontre a propriedade que tem a maior matriz
function findMax(groupResult){ var maxArr = [] var max; for(var item in groupResult){ if(!max) { max = { value:item, count: groupResult[item].length } ; maxArr.push(max); continue; } if(max.count < groupResult[item].length){ maxArr = []; max = { value:item, count: groupResult[item].length } maxArr.push(max) } else if(max === groupResult[item].length) maxArr.push({ value:item, count: groupResult[item].length }) } return maxArr; }
O código completo parece
const _ = require("underscore") var test = [ 1, 1, 2, 1 ]; var groupResult= _.groupBy(test, (e)=> e); console.log(findMax(groupResult)[0].value); function findMax(groupResult){ var maxArr = [] var max; for(var item in groupResult){ if(!max) { max = { value:item, count: groupResult[item].length } ; maxArr.push(max); continue; } if(max.count < groupResult[item].length){ maxArr = []; max = { value:item, count: groupResult[item].length } maxArr.push(max) } else if(max === groupResult[item].length) maxArr.push({ value:item, count: groupResult[item].length }) } return maxArr; }
fonte
var cats = ['Tom','Fluffy','Tom','Bella','Chloe','Tom','Chloe']; var counts = {}; var compare = 0; var mostFrequent; (function(array){ for(var i = 0, len = array.length; i < len; i++){ var word = array[i]; if(counts[word] === undefined){ counts[word] = 1; }else{ counts[word] = counts[word] + 1; } if(counts[word] > compare){ compare = counts[word]; mostFrequent = cats[i]; } } return mostFrequent; })(cats);
fonte
Com ES6, você pode encadear o método assim:
function findMostFrequent(arr) { return arr .reduce((acc, cur, ind, arr) => { if (arr.indexOf(cur) === ind) { return [...acc, [cur, 1]]; } else { acc[acc.indexOf(acc.find(e => e[0] === cur))] = [ cur, acc[acc.indexOf(acc.find(e => e[0] === cur))][1] + 1 ]; return acc; } }, []) .sort((a, b) => b[1] - a[1]) .filter((cur, ind, arr) => cur[1] === arr[0][1]) .map(cur => cur[0]); } console.log(findMostFrequent(['pear', 'apple', 'orange', 'apple'])); console.log(findMostFrequent(['pear', 'apple', 'orange', 'apple', 'pear']));
Se dois elementos tiverem a mesma ocorrência, ele retornará os dois. E funciona com qualquer tipo de elemento.
fonte
arr
dentro de um escopo onde essa variável já está definida como um parâmetro. Isso pode levar a bugs dependendo do navegador usado.arr
é referido porarr.indexOf(cur)
? O parâmetro superior ou o que está dentro de reduzir ??