Add stats
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
|||||||
VaCardActions,
|
VaCardActions,
|
||||||
VaCardContent,
|
VaCardContent,
|
||||||
VaCardTitle,
|
VaCardTitle,
|
||||||
|
VaDivider,
|
||||||
VaForm,
|
VaForm,
|
||||||
VaIcon,
|
VaIcon,
|
||||||
VaInput
|
VaInput
|
||||||
@@ -88,6 +89,7 @@ function add() {
|
|||||||
>Gesehen</VaButton
|
>Gesehen</VaButton
|
||||||
>
|
>
|
||||||
</VaForm>
|
</VaForm>
|
||||||
|
<VaDivider />
|
||||||
<CarDetail :car="enteredType" />
|
<CarDetail :car="enteredType" />
|
||||||
</VaCardContent>
|
</VaCardContent>
|
||||||
</VaCard>
|
</VaCard>
|
||||||
|
|||||||
+64
-2
@@ -1,7 +1,69 @@
|
|||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import StatsBlock from '@/components/StatsBlock.vue';
|
||||||
|
import { type, useTramStore } from '@/stores/tram.ts';
|
||||||
|
import { groupBy } from '@/utils/array.ts';
|
||||||
|
import { idsOf } from '@/utils/typeGroup.ts';
|
||||||
|
import { VaCard, VaCardContent, VaCardTitle } from 'vuestic-ui';
|
||||||
|
|
||||||
|
const tramStore = useTramStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<h1>Stats</h1>
|
<article class="p-4">
|
||||||
|
<h1 class="mb-4 text-3xl">Statistiken</h1>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
<VaCard stripe-color="success" stripe>
|
||||||
|
<VaCardTitle>Nach Typ</VaCardTitle>
|
||||||
|
<VaCardContent>
|
||||||
|
<div class="flex flex-col justify-around gap-4">
|
||||||
|
<div v-for="(group, i) in groupBy(type, (t) => t[3])" :key="i">
|
||||||
|
<p>{{ idsOf(group.elements) }}:</p>
|
||||||
|
<StatsBlock
|
||||||
|
:total="group.elements.reduce((acc, t) => acc + (t[1] - t[0]) + 1, 0)"
|
||||||
|
:found="
|
||||||
|
tramStore.trams.bsag.filter((id) =>
|
||||||
|
group.elements.some((t) => id >= t[0] && id <= t[1])
|
||||||
|
).length
|
||||||
|
"
|
||||||
|
:icon="group.elements[0][2] ? 'tram' : 'directions_bus'"
|
||||||
|
:color="group.elements[0][2] ? 'danger' : 'warning'"
|
||||||
|
:name="group.key"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</VaCardContent>
|
||||||
|
</VaCard>
|
||||||
|
<VaCard class="mb-auto" stripe-color="success" stripe>
|
||||||
|
<VaCardTitle>Quick stats</VaCardTitle>
|
||||||
|
<VaCardContent>
|
||||||
|
<div class="flex flex-col justify-around sm:flex-row">
|
||||||
|
<StatsBlock
|
||||||
|
:total="tramStore.shortStats.totalCount"
|
||||||
|
:found="tramStore.shortStats.found"
|
||||||
|
name="Fahrzeuge"
|
||||||
|
color="success"
|
||||||
|
icon="train"
|
||||||
|
/>
|
||||||
|
<StatsBlock
|
||||||
|
:total="tramStore.shortStats.tramCount"
|
||||||
|
:found="tramStore.shortStats.trams"
|
||||||
|
name="Trams"
|
||||||
|
color="danger"
|
||||||
|
icon="tram"
|
||||||
|
/>
|
||||||
|
<StatsBlock
|
||||||
|
:total="tramStore.shortStats.busCount"
|
||||||
|
:found="tramStore.shortStats.busses"
|
||||||
|
name="Busse"
|
||||||
|
color="warning"
|
||||||
|
icon="directions_bus"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VaCardContent>
|
||||||
|
</VaCard>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
|||||||
+3
-3
@@ -1,12 +1,12 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { computed, reactive } from 'vue';
|
import { computed, reactive } from 'vue';
|
||||||
|
|
||||||
export const type: [number, number, boolean, string][] = [
|
export type CarType = [number, number, boolean, string];
|
||||||
|
export const type: CarType[] = [
|
||||||
[3101, 3143, true, 'GT8N-1 - Bombardier Flexity Classic'],
|
[3101, 3143, true, 'GT8N-1 - Bombardier Flexity Classic'],
|
||||||
[3201, 3249, true, 'GT8N-2 - Siemens Avenio'],
|
[3201, 3249, true, 'GT8N-2 - Siemens Avenio'],
|
||||||
[3401, 3435, true, 'GT8N-2 - Siemens Avenio'],
|
[3401, 3435, true, 'GT8N-2 - Siemens Avenio'],
|
||||||
[4001, 4005, false, 'Solobus Mercedes eCitaro O 530 C2'],
|
[4001, 4020, false, 'Solobus Mercedes eCitaro O 530 C2'],
|
||||||
[4006, 4020, false, 'Solobus Mercedes eCitaro O 530 C2'],
|
|
||||||
[4061, 4075, false, 'Solobus Mercedes Citaro O 530 C2'],
|
[4061, 4075, false, 'Solobus Mercedes Citaro O 530 C2'],
|
||||||
[4076, 4090, false, 'Solobus Mercedes Citaro O 530 C2 hybrid'],
|
[4076, 4090, false, 'Solobus Mercedes Citaro O 530 C2 hybrid'],
|
||||||
[4150, 4151, false, 'Solobus MAN Lion´s City A21 NL 280'],
|
[4150, 4151, false, 'Solobus MAN Lion´s City A21 NL 280'],
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
/**
|
||||||
|
* Returns the sum of all the elements of a numeric array.
|
||||||
|
* @param arr Numeric array.
|
||||||
|
*/
|
||||||
|
export function sumArray(arr: ReadonlyArray<number>): number {
|
||||||
|
return sumBy(arr, (a) => a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sum of all the elements of any array,
|
||||||
|
* given a lamda function to receive the numeric representation of each element.
|
||||||
|
* @param arr Any array.
|
||||||
|
* @param by Map element to number or string, which will be interpreted as a number.
|
||||||
|
*/
|
||||||
|
export function sumBy<E>(arr: ReadonlyArray<E>, by: (element: E) => number | string): number {
|
||||||
|
return arr.reduce((sum, element) => sum + Number(by(element)), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the product of a numeric array.
|
||||||
|
* @param arr Numeric array.
|
||||||
|
*/
|
||||||
|
export function multArray(arr: ReadonlyArray<number>): number {
|
||||||
|
return multBy(arr, (a) => a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the product of any array,
|
||||||
|
* given a lamda function to receive the numeric representation of each element.
|
||||||
|
* @param arr Any array.
|
||||||
|
* @param by Map element to number or string, which will be interpreted as a number.
|
||||||
|
*/
|
||||||
|
export function multBy<E>(arr: ReadonlyArray<E>, by: (element: E) => number | string): number {
|
||||||
|
if (arr.length <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return arr.reduce((sum, element) => sum * Number(by(element)), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Group an array by a given key.
|
||||||
|
* @param arr Any array.
|
||||||
|
* @param by Map element to key.
|
||||||
|
* @param equals Check if two keys are identical. Defaults to strict equality.
|
||||||
|
*/
|
||||||
|
export function groupBy<E, K>(
|
||||||
|
arr: ReadonlyArray<E>,
|
||||||
|
by: (element: E) => K,
|
||||||
|
equals: (a: K, b: K) => boolean = (a, b) => a === b
|
||||||
|
): { key: K; elements: Array<E> }[] {
|
||||||
|
return arr.reduce(
|
||||||
|
(groups, element) => {
|
||||||
|
const key = by(element);
|
||||||
|
const index = groups.findIndex((e) => equals(e.key, key));
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
// New key found, add to group array.
|
||||||
|
groups.push({ key, elements: [element] });
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = groups[index];
|
||||||
|
existing.elements.push(element);
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
},
|
||||||
|
[] as { key: K; elements: Array<E> }[]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the middle Element of an array, rounding up.
|
||||||
|
* Returns undefined for empty arrays without throwing!
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* getMiddleElement([1, 2, 3, 4]) === 3
|
||||||
|
* getMiddleElement([1, 2, 3]) === 2
|
||||||
|
* getMiddleElement([1]) === 1
|
||||||
|
* getMiddleElement([]) === undefined
|
||||||
|
* ```
|
||||||
|
* @param arr
|
||||||
|
*/
|
||||||
|
export function getMiddleElement<E>(arr: ReadonlyArray<E>) {
|
||||||
|
return arr[Math.floor(arr.length / 2)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with every combination of every element in an array.
|
||||||
|
* The combination array will have a length of `n * (n-1)` for an array of length `n`.
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const arr = [0, 1, 2]
|
||||||
|
*
|
||||||
|
* combinations(arr) === [
|
||||||
|
* {a: 0, b: 1},
|
||||||
|
* {a: 0, b: 2},
|
||||||
|
* {a: 1, b: 0},
|
||||||
|
* {a: 1, b: 2},
|
||||||
|
* {a: 2, b: 0},
|
||||||
|
* {a: 2, b: 1},
|
||||||
|
* ]
|
||||||
|
* ```
|
||||||
|
* @param arr
|
||||||
|
*/
|
||||||
|
export function combinations<E>(arr: ReadonlyArray<E>): { a: E; b: E }[] {
|
||||||
|
return arr
|
||||||
|
.flatMap((a, _, arr) => arr.flatMap((b) => (a !== b ? [{ a, b }] : null)))
|
||||||
|
.filter((c) => !!c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array with every unique combination of every element in an array.
|
||||||
|
* The combination array will have a length of `n! / ((n-2)! * 2)` for an array of length `n`.
|
||||||
|
* @example
|
||||||
|
* ```typescript
|
||||||
|
* const arr = [0, 1, 2]
|
||||||
|
*
|
||||||
|
* combinations(arr) === [
|
||||||
|
* {a: 0, b: 1},
|
||||||
|
* {a: 0, b: 2},
|
||||||
|
* {a: 1, b: 2}
|
||||||
|
* ]
|
||||||
|
* ```
|
||||||
|
* @param arr
|
||||||
|
*/
|
||||||
|
export function uniqueCombinations<E>(arr: ReadonlyArray<E>): { a: E; b: E }[] {
|
||||||
|
return arr.flatMap((a, aI) => arr.flatMap((b, bI) => (a !== b && aI > bI ? [{ a, b }] : [])));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a filter lambda to be used in array.filter() to only allow the
|
||||||
|
* first occurrence of duplicate values.
|
||||||
|
* @param isEqual Custom equality check, defaults to `a === b`
|
||||||
|
*/
|
||||||
|
export function unique<E>(isEqual: (a: E, b: E) => boolean = (a, b) => a === b) {
|
||||||
|
return (self: E, index: number, arr: ReadonlyArray<E>) =>
|
||||||
|
arr.findIndex((v) => isEqual(v, self)) === index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace a char in a two-dimensional array of strings.
|
||||||
|
* @param arr two-dimensional string array
|
||||||
|
* @param x column
|
||||||
|
* @param y row
|
||||||
|
* @param char Char
|
||||||
|
*/
|
||||||
|
export function replaceChar(arr: string[], x: number, y: number, char: string) {
|
||||||
|
arr[y] = arr[y].substring(0, x) + char + arr[y].substring(x + char.length);
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import type { CarType } from '@/stores/tram.ts';
|
||||||
|
|
||||||
|
export function idsOf(elements: CarType[]): string {
|
||||||
|
if (elements.length <= 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return elements.map((t) => (t[0] === t[1] ? t[0] : `${t[0]}-${t[1]}`)).join(', ');
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user