Je cherche un moyen élégant de déterminer quel élément a la plus haute occurrence ( mode ) dans un tableau JavaScript.
Par exemple, dans
['pear', 'Apple', 'orange', 'Apple']
l'élément 'Apple'
est l'élément le plus fréquent.
Ceci est juste le mode. Voici un rapide, non optimisé Solution. Ce devrait être 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;
}
Il y a eu quelques développements en javascript depuis 2009 - je pensais ajouter une autre option. Je suis moins préoccupé par l'efficacité jusqu'à ce que ce soit réellement un problème, alors ma définition du code {"élégant"}} _ (comme stipulé par l'OP) favorise la lisibilité - ce qui est bien sûr subjectif ...
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
Dans cet exemple particulier, si deux éléments ou plus de l'ensemble ont des occurrences égales, celui qui apparaît le plus tard dans le tableau sera renvoyé. Il est également intéressant de noter que cela modifiera votre tableau d'origine - ce qui peut être évité si vous le souhaitez avec un appel préalable de Array.slice
.
Edit: a mis à jour l'exemple avec quelques ES6grosses flèches parce que 2015 s'est passé et je pense qu'elles sont jolies ... Si vous êtes préoccupé par la compatibilité en amont, vous peut trouver cela dans historique de révision .
Conformément à la demande George Jempty's
demandant à ce que l'algorithme tienne compte des liens, je propose une version modifiée de l'algorithme Matthew Flaschen's
.
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;
}
Cela retournera maintenant une chaîne avec le ou les éléments mode délimités par un symbole '&'
. Lorsque le résultat est reçu, il peut être divisé sur cet élément '&'
et vous avez votre (vos) mode (s).
Une autre option serait de renvoyer un tableau d’éléments de mode comme ceci:
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;
}
Dans l'exemple ci-dessus, vous pourrez alors traiter le résultat de la fonction sous la forme d'un tableau de modes.
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] }
}
Sur la base de la réponse ES6 + de Emissary , vous pouvez utiliser Array.prototype.reduce
pour faire votre comparaison (par opposition à trier, éjecter et potentiellement transformer votre tableau), ce qui, à mon avis, semble assez lisse.
const mode = (myArray) =>
myArray.reduce(
(a,b,i,arr)=>
(arr.filter(v=>v===a).length>=arr.filter(v=>v===b).length?a:b),
null)
La valeur par défaut est null, ce qui ne vous donnera pas toujours une réponse véridique si null est une option possible pour laquelle vous filtrez, peut-être que ce pourrait être un deuxième argument facultatif.
L'inconvénient, à l'instar de diverses autres solutions, est qu'il ne gère pas les «états d'étirage», mais cela pourrait toujours être réalisé avec une fonction de réduction légèrement plus complexe.
Essayer une approche déclarative ici. Cette solution construit un objet pour compiler les occurrences de chaque mot. Filtre ensuite l'objet en un tableau en comparant le nombre total d'occurrences de chaque mot à la valeur la plus élevée trouvée dans l'objet.
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));
Voici ma solution à ce problème, mais avec des chiffres et en utilisant la nouvelle fonctionnalité «Set». Ce n’est pas très performant, mais j’ai eu beaucoup de plaisir à écrire cela et il supporte plusieurs valeurs maximales.
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]
En passant, n'utilisez pas ceci pour la production, ceci est juste une illustration de la façon dont vous pouvez le résoudre avec uniquement les fonctions ES6 et Array.
Comme j'utilise cette fonction en tant que questionnaire pour les interviewers, je publie ma solution:
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))
Cette solution peut renvoyer plusieurs éléments d'un tableau s'ils se produisent pendant la même heure. par exemple, un tableau arr = [3,4,3,6,4] a deux valeurs de mode, 3 et 6.
Voici la solution,
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;
}
Essayez aussi, cela ne prend pas en compte la version du navigateur.
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']));
Veuillez noter que cette fonction renvoie la dernière occurrence dans le tableau quand 2 ou plusieurs entrées apparaissent le même nombre de fois!
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
}
Temps pour une autre solution:
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;
}
Si la brièveté compte (ce n'est pas le cas), alors:
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;
}
Si des membres inexistants doivent être évités (par exemple un tableau fragmenté), un test supplémentaire de hasOwnProperty est requis:
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
D'autres réponses ici retourneront undefined .
var mode = 0;
var c = 0;
var num = new Array();
var value = 0;
var greatest = 0;
var ct = 0;
Remarque: ct est la longueur du tableau.
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;
}
}
Voici ma solution: -
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']))
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;
}
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);
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'))
Voici la version moderne qui utilise des cartes intégrées (elle fonctionne donc plus que ce qui peut être converti en chaînes uniques):
'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']));
Vous pouvez essayer ceci:
// 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;
}
Voici mon chemin. J'essaie de regrouper le poing de données.
const _ = require("underscore")
var test = [ 1, 1, 2, 1 ];
var groupResult = _.groupBy(test, (e)=> e);
Le groupeResult devrait être
{
1: [1, 1, 1]
2: [2]
}
Puis trouver la propriété qui a le plus long tableau
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;
}
Le code complet ressemble à
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;
}
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);
Cette fonction est une fonction générique pour chaque type d’information. Il compte l'occurrence des éléments puis retourne un tableau avec le maximum d'éléments présents
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;
}
Avec ES6, vous pouvez chaîner la méthode comme ceci:
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']));
Si deux éléments ont la même occurrence, les deux seront renvoyés. Et cela fonctionne avec n'importe quel type d'élément.
// 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);
Vous pouvez le résoudre en O(n) complexité
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" );
Il suffit de copier et coller dans la console chrome pour exécuter le code ci-dessus.
Je suppose que vous avez deux approches. Les deux ont des avantages.
Triez ensuite Compter ou Boucle et utilisez une table de hachage pour effectuer le comptage à votre place.
La hashtable est agréable, car une fois le traitement terminé, vous disposez également de tous les éléments distincts. Si vous aviez cependant des millions d'éléments, la table de hachage pourrait utiliser beaucoup de mémoire si le taux de duplication était faible. L'approche de tri, puis de comptage aurait une empreinte mémoire beaucoup plus contrôlable.
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);}