Last active
July 17, 2023 16:30
-
-
Save honboubao/149b6bf2450eb03c28bfc05fe1f623fc to your computer and use it in GitHub Desktop.
concatNullable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Ein fixer String, der in {@link Template} mit anderen Werten verknüpft wird und abhängig von der Gültigkeit der anderen Werte mitangezeigt wird. | |
* | |
* Das Trennzeichen darf kein Empty-String sein! | |
*/ | |
export type Trennzeichen = string; // Empty string als Trennzeichen nicht erlaubt | |
/** | |
* Ein Wert, z.B. von einer Variable, der konkateniert werden soll. Ungültige Werte (null, undefined, Empty-String) werden nicht ausgegeben. | |
* Ungültige Werte beeinflussen ggf. auch die Ausgabe von Trennzeichen im gleichen {@link Template} | |
*/ | |
export type Wert = string | number | null | undefined; | |
/** | |
* Ein Wert oder ein verschachteltes Template, das in einem Template mit anderen Elementen verknüpft wird. | |
*/ | |
export type Einsetzung = Wert | Template; | |
/** | |
* Wird in {@link ConcatTemplate} verwendet, um einzelne Werte direkt zugehörigen Trennzeichen zu konkatenieren. | |
*/ | |
export type WertTemplate = [Wert]; | |
/** | |
* Wenn die Einsetzung einen gültigen Wert hat (nicht null, undefined oder Empty-String), wird der Wert mit den Trennzeichen vorangestellt ausgegeben. | |
* Ansonsten wird nichts (Empty-String) ausgegeben. | |
*/ | |
export type PrefixTemplate = [Trennzeichen, Einsetzung]; | |
/** | |
* Wenn die Einsetzung einen gültigen Wert hat (nicht null, undefined oder Empty-String), wird der Wert mit den Trennzeichen nachgestellt ausgegeben. | |
* Ansonsten wird nichts (Empty-String) ausgegeben. | |
*/ | |
export type SuffixTemplate = [Einsetzung, Trennzeichen]; | |
/** | |
* Wenn beide Einsetzungen einen gültigen Wert haben (nicht null, undefined oder Empty-String), werden die Wert mit den Trennzeichen verbunden. | |
* Wenn nur eine Einsetzung einen gültigen Wert hat, wird dieser unverändert augegeben. | |
* Ansonsten wird nichts (Empty-String) ausgegeben. | |
*/ | |
export type InfixTemplate = [Einsetzung, Trennzeichen, Einsetzung]; | |
/** | |
* Wenn die Einsetzung einen gültigen Wert hat (nicht null, undefined oder Empty-String), wird der Wert mit den Trennzeichen umgeben. | |
* Ansonsten wird nichts (Empty-String) ausgegeben. | |
*/ | |
export type CircumfixTemplate = [Trennzeichen, Einsetzung, Trennzeichen]; | |
/** | |
* Konkateniert alle Templates im Array, die einen gültigen Wert ergeben (nicht null, undefined oder Empty-String). | |
* | |
* Da das Array nur Templates beinhalten kann, muss für einen Wert, der direkt konkateniert werden soll, dieser | |
* als Single-Element-Array (als {@link WertTemplate}) angegeben werden. | |
*/ | |
export type ConcatTemplate = Template[]; | |
/** | |
* Template für {@link concatNullable} mit Werten und Trennzeichen. Der Aufbau des Templates (welche Templates genau, wie verschachtelt angegeben werden) | |
* bestimmt, welche Werte und Trennzeichen und welchen Bedingungen angezeigt werden. | |
* | |
* Trennzeichen sind dabei non-nullable Strings. Empty-Strings sind als Trennzeichen nicht erlaubt, das Verhalten mit Empty-Strings als Leerzeichen ist nicht definiert. | |
* Werte können string, number, null und undefined sein. | |
*/ | |
export type Template = WertTemplate | PrefixTemplate | SuffixTemplate | InfixTemplate | CircumfixTemplate | ConcatTemplate; | |
function isBlank(s: unknown): boolean { | |
return s == null || s === ''; | |
} | |
/** | |
* Konkateniert Werte und Trennzeichen zu einem String, wobei null, undefined und Empty-String Werte nicht ausgegeben werden und | |
* je nach Template-Aufbau zugehörige Trennzeichen ebenfalls ausgelassen werden. | |
* | |
* Siehe auch {@link Template} | |
*/ | |
export function concatNullable(template: Template): string { | |
// verschachtelte Templates rekursiv zu Strings auflösen | |
const parts = template.map((p) => { | |
if (!Array.isArray(p)) { | |
return isBlank(p) ? '' : String(p); | |
} | |
return concatNullable(p); | |
}); | |
// Concat Template, i.e. nur Einsetzungen (oder leeres Array) -> alles übernehmen, was nicht null ist | |
if (template.every(Array.isArray)) { | |
return parts.join(''); | |
} | |
const [blank0, blank1, blank2] = parts.map(isBlank); | |
if (parts.length === 1) { | |
// Value Einsetzung innerhalb eines Concatss -> einfach den Wert übernehmen, wenn es nicht null ist | |
return blank0 ? '' : parts[0]; | |
} | |
if (parts.length === 2) { | |
// Prefix oder Suffix Template -> wenn die Einsetzung null ist, wird auch das Trennzeichen verworfen... | |
if (blank0 || blank1) { | |
return ''; | |
} | |
// ...ansonsten wird die Einsetzung zusammen mit dem Trennzeichen angezeigt | |
return parts.join(''); | |
} else if (parts.length === 3) { | |
// Infix Template beide Einsetzungen null oder Circumfix Template Einsetzung leer -> Einsetzungen mitsamt Trennzeichen verwerfen | |
if ((blank0 && blank2) || blank1) { | |
return ''; | |
} | |
// Infix Template eine der beiden Einsetzungen null -> die jeweils andere Einsetzung ohne Trennzeichen anzeigen | |
if (blank0) { | |
return parts[2]; | |
} | |
if (blank2) { | |
return parts[0]; | |
} | |
// Infix Template mit beiden Einsetzungen oder Circumfix mit Einsetzung vorhanden -> Einsetzungen mit Trennzeichen anzeigen | |
return parts.join(''); | |
} else { | |
// eslint-disable-next-line no-console | |
console.warn('Invalid arguments for concatNullable:', parts); | |
return ''; | |
} | |
} | |
describe('string-utils: concatNullable', () => { | |
describe('mit Benutzer-String', () => { | |
let abteilung: string | null; | |
let vorname: string | null; | |
let nachname: string | null; | |
let durchwahl: string | null; | |
let userId: string | null; | |
let vsnr: string | null; | |
let alter: number | null; | |
const expectStringToBe = (expected: string): void => { | |
// Abteilung, Vorname Nachname, Durchwahl (UserID) / VSNR Alter | |
expect(concatNullable([[[[abteilung, ', ', [vorname, ' ', nachname]], ', ', durchwahl], ' ', ['(', userId, ')']], ' / ', [vsnr, ' ', [alter, ' Jahre']]])).toBe( | |
expected | |
); | |
}; | |
beforeEach(() => { | |
abteilung = 'HSWE'; | |
vorname = 'Hans'; | |
nachname = 'Huber'; | |
durchwahl = '123'; | |
userId = '02u12345'; | |
vsnr = '1111111111'; | |
alter = 32; | |
}); | |
// Abteilung, Vorname Nachname, Durchwahl (UserID) / VSNR Alter | |
it('voller String', () => { | |
expectStringToBe('HSWE, Hans Huber, 123 (02u12345) / 1111111111 32 Jahre'); | |
}); | |
it('ohne vsnr', () => { | |
vsnr = null; | |
expectStringToBe('HSWE, Hans Huber, 123 (02u12345) / 32 Jahre'); | |
}); | |
it('ohne alter', () => { | |
alter = null; | |
expectStringToBe('HSWE, Hans Huber, 123 (02u12345) / 1111111111'); | |
}); | |
it('ohne vsnr, alter', () => { | |
vsnr = alter = null; | |
expectStringToBe('HSWE, Hans Huber, 123 (02u12345)'); | |
}); | |
it('ohne abteilung', () => { | |
vsnr = alter = abteilung = null; | |
expectStringToBe('Hans Huber, 123 (02u12345)'); | |
}); | |
it('ohne vorname', () => { | |
vsnr = alter = vorname = null; | |
expectStringToBe('HSWE, Huber, 123 (02u12345)'); | |
}); | |
it('ohne nachname', () => { | |
vsnr = alter = nachname = null; | |
expectStringToBe('HSWE, Hans, 123 (02u12345)'); | |
}); | |
it('ohne durchwahl', () => { | |
vsnr = alter = durchwahl = null; | |
expectStringToBe('HSWE, Hans Huber (02u12345)'); | |
}); | |
it('ohne name, userId', () => { | |
vsnr = alter = vorname = nachname = userId = null; | |
expectStringToBe('HSWE, 123'); | |
}); | |
it('ohne abteilung, name', () => { | |
vsnr = alter = abteilung = vorname = nachname = null; | |
expectStringToBe('123 (02u12345)'); | |
}); | |
it('ohne name, durchwahl', () => { | |
vsnr = alter = vorname = nachname = durchwahl = null; | |
expectStringToBe('HSWE (02u12345)'); | |
}); | |
it('ohne abteilung, durchwahl', () => { | |
vsnr = alter = abteilung = durchwahl = null; | |
expectStringToBe('Hans Huber (02u12345)'); | |
}); | |
it('ohne durchwahl, userId', () => { | |
vsnr = alter = durchwahl = userId = null; | |
expectStringToBe('HSWE, Hans Huber'); | |
}); | |
it('ohne name, userId', () => { | |
vsnr = alter = vorname = nachname = userId = null; | |
expectStringToBe('HSWE, 123'); | |
}); | |
it('ohne abteilung, nachname, durchwahl', () => { | |
vsnr = alter = abteilung = nachname = durchwahl = null; | |
expectStringToBe('Hans (02u12345)'); | |
}); | |
it('ohne abteilung, durchwahl, userId', () => { | |
vsnr = alter = abteilung = durchwahl = userId = null; | |
expectStringToBe('Hans Huber'); | |
}); | |
}); | |
describe('mit Adress-String', () => { | |
let strasse: string | null; | |
let haus: number | null; | |
let stiege: number | null; | |
let tuer: number | null; | |
let plz: string | null; | |
let ort: string | null; | |
const expectStringToBe = (expected: string): void => { | |
// Straße Hausnummer/Stiege/Türnummer, PLZ - Ort | |
expect( | |
concatNullable([ | |
[ | |
[strasse, ' ', haus], | |
['/', stiege], | |
['/', tuer ?? (stiege ? '-' : '')], | |
], | |
', ', | |
[plz, ' - ', ort], | |
]) | |
).toBe(expected); | |
}; | |
beforeEach(() => { | |
strasse = 'Bambuslichtung'; | |
haus = 9; | |
stiege = 2; | |
tuer = 14; | |
plz = '1234'; | |
ort = 'Hinterwaldingen'; | |
}); | |
// Abteilung, Vorname Nachname, Durchwahl (UserID) / VSNR Alter | |
it('voller String', () => { | |
expectStringToBe('Bambuslichtung 9/2/14, 1234 - Hinterwaldingen'); | |
}); | |
it('ohne haus', () => { | |
haus = null; | |
expectStringToBe('Bambuslichtung/2/14, 1234 - Hinterwaldingen'); | |
}); | |
it('ohne stiege', () => { | |
stiege = null; | |
expectStringToBe('Bambuslichtung 9/14, 1234 - Hinterwaldingen'); | |
}); | |
it('ohne tuer', () => { | |
tuer = null; | |
expectStringToBe('Bambuslichtung 9/2/-, 1234 - Hinterwaldingen'); | |
}); | |
it('ohne plz', () => { | |
plz = null; | |
expectStringToBe('Bambuslichtung 9/2/14, Hinterwaldingen'); | |
}); | |
it('ohne ort', () => { | |
ort = null; | |
expectStringToBe('Bambuslichtung 9/2/14, 1234'); | |
}); | |
it('ohne haus, stiege', () => { | |
haus = stiege = null; | |
expectStringToBe('Bambuslichtung/14, 1234 - Hinterwaldingen'); | |
}); | |
it('ohne stiege, tuer', () => { | |
stiege = tuer = null; | |
expectStringToBe('Bambuslichtung 9, 1234 - Hinterwaldingen'); | |
}); | |
it('ohne haus, tuer', () => { | |
haus = tuer = null; | |
expectStringToBe('Bambuslichtung/2/-, 1234 - Hinterwaldingen'); | |
}); | |
it('ohne haus, stiege, tuer', () => { | |
haus = stiege = tuer = null; | |
expectStringToBe('Bambuslichtung, 1234 - Hinterwaldingen'); | |
}); | |
it('ohne plz, ort', () => { | |
plz = ort = null; | |
expectStringToBe('Bambuslichtung 9/2/14'); | |
}); | |
it('ohne haus, stiege, tuer, plz, ort', () => { | |
haus = stiege = tuer = plz = ort = null; | |
expectStringToBe('Bambuslichtung'); | |
}); | |
it('ohne strasse, haus, stiege, tuer', () => { | |
strasse = haus = stiege = tuer = null; | |
expectStringToBe('1234 - Hinterwaldingen'); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment