Also ehrlich gesagt ist die Lösung von CPU meiner Meinung nach sehr kompliziert, und nicht wirklich hilfreich was das Verständnis angeht.
Gerade um eine Lösung verständlich zu halten würde ich das Problem zerlegen.
Zunächst gehen wir mal davon aus, dass unser Array arr heisst:
[src=javascript]
const arr = [
{name: "irgendwas", email: "demo@demo.de", rating: "gut", rating2: "okay", message: "etwas text"},
{name: "irgendwas", email: "demo@demo.de", rating: "gut", rating2: "solala", message: "etwas text"},
{name: "irgendwas", email: "demo@demo.de", rating: "gut", rating2: "okay", message: "etwas text"},
{name: "irgendwas", email: "demo@demo.de", rating: "schlecht", rating2: "okay", message: "etwas text"},
{name: "irgendwas", email: "demo@demo.de", rating: "gut", rating2: "gut", message: "etwas text"},
{name: "irgendwas", email: "demo@demo.de", rating: "mittel", rating2: "okay", message: "etwas text"},
];
[/src]
Nun versuchen wir zunächst ein einfacher Problem zu lösen: Wie viele Einträge mit rating: "gut" gib es? Ein Lösungsansatz ist das hier:
[src=javascript]
const noOfRatingGood = arr.filter(elem => elem.rating === "gut").length;
[/src]
Ein Funktionaler aber dennoch einfach zu verstehender Ansatz. Wir "filtern" das array einfach so, dass wir ein neues Array erhalten, dass nur die Einträge mit rating="gut" enthält. Dann schauen wir mit .length wie lang dieses Array ist. Entsprechend so viele einträge mit dem "gut" rating gibt es.
Das rating "schlecht" geht analog. Nun können wir jedoch eine Funktion schreiben die für jeden rating-string die Anzahl an Einträgen ausgibt:
[src=javascript]
function getNoOfElementsWithRating(arr, rating) {
return arr.filter(elem => elem.rating === rating).length;
}
const noOfRatingGood = getNoOfElementsWithRating(arr, "gut");
const noOfRatingBad = getNoOfElementsWithRating(arr, "schlecht");
[/src]
Nun willst du das jedoch auch für "rating2" machen. Wir können die Funktion hierzu etwas anpassen. Wichtig zu wissen ist dabei, dass obj.key identisch ist zu obj["key"], wobei bei letzterer Version auch eine variable genutzt werden kann: obj[k] ist identisch zu obj["key"] wenn die Variable "k" den Wert "key" hat. Das erlaubt die Funktion etwas allgemeiner zu schreiben:
[src=javascript]
function getNoOfElementsWithRating(arr, field, rating) {
return arr.filter(elem => elem[field] === rating).length;
}
const noOfRatingGood = getNoOfElementsWithRating(arr, "rating", "gut");
const noOfRatingBad = getNoOfElementsWithRating(arr, "rating", "schlecht");
const noOfRating2Good = getNoOfElementsWithRating(arr, "rating2", "gut");
const noOfRating2Bad = getNoOfElementsWithRating(arr, "rating2", "schlecht");
[/src]
Wenn du nun ein Array der möglichen ratings hast:
[src=javascript]
const ratings = ["gut", "schlecht", "mittel"];
[/src]
Können wir das und "reduce" benutzen um daraus ein Objekt dieser Art zu erzeugen:
[src=javascript]
{ gut: 4, schlecht: 1, mittel: 1 }
[/src]
"reduce" ist etwas kompliziert, aber durchaus einen Blick wert, da es ein Kernkonzept der funktionalen Programmierung ist. Wir machen folgendes:
[src=javascript]
const allRatings = ratings.reduce((acum, rating) => {
acum[rating] = getNoOfElementsWithRating(arr, "rating", rating);
return acum;
}, {});
[/src]
Reduce nimmt 2 parameter: Der zweite ist ein sogenannter initialwert für den Akkumulator. Der erste Parameter ist eine Funktion, die für jeden Eintrag in dem Array ein Mal aufgerufen wird. Sie hat widerrum 2 Parameter. Der zweite ist jeweils das Element in dem Array, und der erste ist am Anfang der initialwert für den Akkumulator, und dann jeweils der Rückgabewert des vorherigen Aufrufs der Funktion. Der letzte Rückgabewert wird dann von "reduce" zurückgegeben. Man muss da vlt. mal etwas mit rumspielen, aber das lohnt sich echt!
Dank ES6 können wir das ganze auch noch etwas kürzer schreiben:
[src=javascript]
const allRatings = ratings.reduce((acum, rating) => ({...acum, [rating]: getNoOfElementsWithRating(arr, "rating", rating) }), {});
[/src]
Wir benutzen 2 features: "Computed property keys" und den "Object spread operator". Das googelt man vermutlich am besten.
Man möchte nun u.U. noch die "ratings" automatisch ermitteln. Es ist nun sehr einfach in unserem originalen array einfach nur die werte von "rating" zu extrahieren:
[src=javascript]
const ratings =arr.map(elem => elem.rating);
[/src]
nur enthält das noch Dopplungen. Also quasi das hier:
[src=javascript]
gut
gut
gut
schlecht
gut
mittel
[/src]
Dopplungen können wir nun mittels einem "Set" sehr einfach entfernen. Ein Set ist so was ähnliches wie ein Array, aber ohne Dopplungen. Wir machen also:
[src=javascript]
const ratings = new Set(arr.map(elem => elem.rating));
[/src]
Da jetzt ratings aber ein Set ist und kein array ist müssen wir das wieder in ein Array umwandeln. Hier kann ES6 spread syntax verwendet werden. Die macht in dieser Variante aus irgendwas iterierbarem ein Array:
[src=javascript]
const ratings = [...new Set(arr.map(elem => elem.rating))];
[/src]
Das alles können wir wieder zu einer Funktion zusammenfassen:
[src=javascript]
function getRatings(arr) {
return [...new Set(arr.map(elem => elem.rating))]
.reduce((acum, rating) => ({...acum, [rating]: getNoOfElementsWithRating(arr, "rating", rating) }), {});
}
[/src]
Und wir machen die wieder generisch, so dass wir sie für "rating" und "rating2" benutzen können:
[src=javascript]
function getRatings(arr, field) {
return [...new Set(arr.map(elem => elem[field]))]
.reduce((acum, rating) => ({...acum, [rating]: getNoOfElementsWithRating(arr, field, rating) }), {});
}
[/src]
Die komplette Lösung ist nun:
[src=javascript]
const arr = [
{name: "irgendwas", email: "demo@demo.de", rating: "gut", rating2: "okay", message: "etwas text"},
{name: "irgendwas", email: "demo@demo.de", rating: "gut", rating2: "solala", message: "etwas text"},
{name: "irgendwas", email: "demo@demo.de", rating: "gut", rating2: "okay", message: "etwas text"},
{name: "irgendwas", email: "demo@demo.de", rating: "schlecht", rating2: "okay", message: "etwas text"},
{name: "irgendwas", email: "demo@demo.de", rating: "gut", rating2: "gut", message: "etwas text"},
{name: "irgendwas", email: "demo@demo.de", rating: "mittel", rating2: "okay", message: "etwas text"},
];
function getRatings(arr, field) {
return [...new Set(arr.map(elem => elem[field]))]
.reduce((acum, rating) => ({...acum, [rating]: getNoOfElementsWithRating(arr, field, rating) }), {});
}
function getNoOfElementsWithRating(arr, field, rating) {
return arr.filter(elem => elem[field] === rating).length;
}
const rating = getRatings(arr, "rating");
const rating2 = getRatings(arr, "rating2");
[/src]
Kurz noch ein Wort zu var, let und const:
Ich würde immer const benutzen wenn es geht! Man hat die Garantie, dass der Wert nicht "ausversehen" überschrieben wird. Will man das benutzt man let. Dadurch ist das immer explizit.
Wenn wirklich ein Browser verwendet werden soll der kein let/const hat benutzt babel! Der code ist mit let/const um vieles leserlicher und sicherer!