javascript - ejemplos - ambigüedad
Encuentra idiomáticamente la cantidad de ocurrencias que un valor dado tiene en una matriz (10)
Tengo una matriz con valores repetitivos. Me gustaría encontrar el número de ocurrencias para cualquier valor dado.
Por ejemplo, si tengo una matriz definida como tal: var dataset = [2,2,4,2,6,4,7,8];
, Quiero encontrar el número de ocurrencias de un cierto valor en la matriz. Es decir, el programa debería mostrar que si tengo 3 ocurrencias del valor 2
, 1 ocurrencia del valor 6
, y así sucesivamente.
¿Cuál es la forma más idiomática / elegante de hacer esto?
Aquí hay una manera de mostrar TODOS los conteos de una vez:
var dataset = [2, 2, 4, 2, 6, 4, 7, 8];
var counts = {}, i, value;
for (i = 0; i < dataset.length; i++) {
value = dataset[i];
if (typeof counts[value] === "undefined") {
counts[value] = 1;
} else {
counts[value]++;
}
}
console.log(counts);
// Object {
// 2: 3,
// 4: 2,
// 6: 1,
// 7: 1,
// 8: 1
//}
Me pareció más útil terminar con una lista de objetos con una clave para lo que se está contando y una clave para el conteo:
const data = [2,2,4,2,6,4,7,8]
let counted = []
for (var c of data) {
const alreadyCounted = counted.map(c => c.name)
if (alreadyCounted.includes(c)) {
counted[alreadyCounted.indexOf(c)].count += 1
} else {
counted.push({ ''name'': c, ''count'': 1})
}
}
console.log(counted)
que devuelve:
[ { name: 2, count: 3 },
{ name: 4, count: 2 },
{ name: 6, count: 1 },
{ name: 7, count: 1 },
{ name: 8, count: 1 } ]
No es el método más limpio, y si alguien sabe cómo lograr el mismo resultado con reduce
házmelo saber. Sin embargo, produce un resultado con el que es bastante fácil trabajar.
Navegadores más nuevos solo debido al uso de Array.filter
var dataset = [2,2,4,2,6,4,7,8];
var search = 2;
var occurrences = dataset.filter(function(val) {
return val === search;
}).length;
console.log(occurrences); // 3
Primero, puedes usar Brute Force Solution yendo con Linear Search.
public int LinearSearchcount(int[] A, int data){
int count=0;
for(int i=0;i<A.length;i++) {
if(A[i]==data) count++;
}
return count;
}
Sin embargo, para seguir con esto, obtenemos la complejidad del tiempo como O (n). Pero yendo con la búsqueda binaria, podemos mejorar nuestra Complejidad.
Puede contar todos los elementos en una matriz, en una sola línea, usando reducir.
[].reduce((a,b) => (a[b] = a[b] + 1 || 1) && a, {})
Esto producirá un objeto, cuyas claves son los elementos distintivos de la matriz y los valores son el recuento de las ocurrencias de los elementos en la matriz. A continuación, puede acceder a uno o más de los recuentos accediendo a una clave correspondiente en el objeto.
Por ejemplo, si envuelve lo anterior en una función llamada count()
:
function count(arr) {
return arr.reduce((a,b) => (a[b] = a[b] + 1 || 1) && a, {})
}
count([''example'']) // { example: 1 }
count([2,2,4,2,6,4,7,8])[2] // 3
Puede hacerlo con el uso del array.reduce(callback[, initialValue])
en JavaScript 1.8
var dataset = [2,2,4,2,6,4,7,8],
dataWithCount = dataset.reduce( function( o , v ) {
if ( ! o[ v ] ) {
o[ v ] = 1 ;
} else {
o[ v ] = o[ v ] + 1;
}
return o ;
}, {} );
// print data with count.
for( var i in dataWithCount ){
console.log( i + ''occured '' + dataWithCount[i] + ''times '' );
}
// find one number
var search = 2,
count = dataWithCount[ search ] || 0;
Usando un bucle normal, puede encontrar las ocurrencias de manera consistente y confiable:
const dataset = [2,2,4,2,6,4,7,8];
function getNumMatches(array, valToFind) {
let numMatches = 0;
for (let i = 0, j = array.length; i < j; i += 1) {
if (array[i] === valToFind) {
numMatches += 1;
}
}
return numMatches;
}
alert(getNumMatches(dataset, 2)); // should alert 3
DEMO: https://jsfiddle.net/a7q9k4uu/
Para hacerlo más genérico, la función podría aceptar una función de predicado con lógica personalizada (que devuelva true
/ false
) que determinaría el recuento final. Por ejemplo:
const dataset = [2,2,4,2,6,4,7,8];
function getNumMatches(array, predicate) {
let numMatches = 0;
for (let i = 0, j = array.length; i < j; i += 1) {
const current = array[i];
if (predicate(current) === true) {
numMatches += 1;
}
}
return numMatches;
}
const numFound = getNumMatches(dataset, (item) => {
return item === 2;
});
alert(numFound); // should alert 3
reduce
es más apropiado aquí que el filter
ya que no crea una matriz temporal solo para contar.
var dataset = [2,2,4,2,6,4,7,8];
var search = 2;
var count = dataset.reduce(function(n, val) {
return n + (val === search);
}, 0);
console.log(count);
Tenga en cuenta que es fácil extender eso para usar un predicado de coincidencia personalizado, por ejemplo, para contar objetos que tienen una propiedad específica:
people = [
{name: ''Mary'', gender: ''girl''},
{name: ''Paul'', gender: ''boy''},
{name: ''John'', gender: ''boy''},
{name: ''Lisa'', gender: ''girl''},
{name: ''Bill'', gender: ''boy''},
{name: ''Maklatura'', gender: ''girl''}
]
var numBoys = people.reduce(function (n, person) {
return n + (person.gender == ''boy'');
}, 0);
console.log(numBoys);
Contar todos los elementos, es decir, hacer un objeto como {x:count of xs}
es complicado en javascript, porque las claves de objetos solo pueden ser cadenas, por lo que no se puede contar confiablemente una matriz con tipos mixtos. Aún así, la siguiente solución simple funcionará bien en la mayoría de los casos:
count = function (ary, classifier) {
classifier = classifier || String;
return ary.reduce(function (counter, item) {
var p = classifier(item);
counter[p] = counter.hasOwnProperty(p) ? counter[p] + 1 : 1;
return counter;
}, {})
};
people = [
{name: ''Mary'', gender: ''girl''},
{name: ''Paul'', gender: ''boy''},
{name: ''John'', gender: ''boy''},
{name: ''Lisa'', gender: ''girl''},
{name: ''Bill'', gender: ''boy''},
{name: ''Maklatura'', gender: ''girl''}
];
// If you don''t provide a `classifier` this simply counts different elements:
cc = count([1, 2, 2, 2, 3, 1]);
console.log(cc);
// With a `classifier` you can group elements by specific property:
countByGender = count(people, function (item) {
return item.gender
});
console.log(countByGender);
Actualización 2017
En ES6, usa el objeto Map
para contar confiablemente objetos de tipos arbitrarios:
class Counter extends Map {
constructor(iter, key=null) {
super();
this.key = key || (x => x);
for (let x of iter) {
this.add(x);
}
}
add(x) {
x = this.key(x);
this.set(x, (this.get(x) || 0) + 1);
}
}
// again, with no classifier just count distinct elements
results = new Counter([1, 2, 3, 1, 2, 3, 1, 2, 2]);
for (let [number, times] of results.entries())
console.log(''%s occurs %s times'', number, times);
// counting objects
people = [
{name: ''Mary'', gender: ''girl''},
{name: ''John'', gender: ''boy''},
{name: ''Lisa'', gender: ''girl''},
{name: ''Bill'', gender: ''boy''},
{name: ''Maklatura'', gender: ''girl''}
];
chessChampions = {
2010: people[0],
2012: people[0],
2013: people[2],
2014: people[0],
2015: people[2],
};
results = new Counter(Object.values(chessChampions));
for (let [person, times] of results.entries())
console.log(''%s won %s times'', person.name, times);
// you can also provide a classifier as in the above
byGender = new Counter(people, x => x.gender);
for (let g of [''boy'', ''girl''])
console.log("there are %s %ss", byGender.get(g), g);
array.filter(c => c === searchvalue).length;
var dataset = [2,2,4,2,6,4,7,8], count = {}
dataset.forEach(function(el){
count[el] = count[el] + 1 || 1
});
console.log(count)
// {
// 2: 3,
// 4: 2,
// 6: 1,
// 7: 1,
// 8: 1
// }