Raketenwilli: Letzte Verschlimmbesserungen ( CharCounter() )

Beitrag lesen

(Eigentlich ging es ja um die Anzahl der Klammern in Deinem scriptlet …)

"use strict";

function CharCounter( haystack, needles, log ) {

	/* CharCounter() count chars in a string grouped by char.
	 * * Args
	 * 	- Arg #1: var haystack:
	 * 		Must set.
	 * 		The string.
	 * 
	 * 	- Optional arg #2: var needles:
	 * 		A string or array with the chars which will be grouped and count.
	 * 		If it is not set, all occurring(sic!) characters in haystack are counted.
	 * 
	 * 	- Optional arg #2: var log:
	 * 		Truly or falsy: Shows the result (sorted) in the console?
	 * 
	 * 	- vars from parent envirement:
	 * 	 -  optional: var CharCounterLogDefault 
	 * 			This is a option (must not defined): true or false
	 *			The arg #3 overwrite this. If never set the value is false
	 * 
	 * * Returns a object a.k.a. { a:1,o:2,'3':1 } 
	 *   Object.keys are grouped but not sorted.
	*/

	if( typeof log == 'undefined') {
		if( typeof CharCounterLogDefault != 'undefined' ) {
			log = CharCounterLogDefault;
		} else {
			log = false;
		}
	} 

	if( arguments.length < 1 ) {
		throw new Error( 
			'CharCounter has 3 args:'  + "\n"
		+ 	'- haystack(type: string),' + "\n"
		+   '- needles(string or array).' + "\n"
		+   'If needles not given it count all chars in haystack.' + "\n"
		+   'The last arg is log (truly or falsy). If truely CharCounter' + "\n"
		+	'print into the console.'
		);
	}
	
	if( 'string' != typeof haystack ) {
		throw new Error('Argument 1 (haystack) is not astring!');
	}	
	
	if( arguments.length == 1 ) {
		needles = haystack;
	}	

	var counter = {};

	if(	
		   null  === needles
		|| false === needles
		|| true  === needles
		|| ''     == needles
	) {
		throw new Error( 'Error: needles is exact null, true, false - or empty' ) ; 
	}

	if( 'string' == typeof needles ) {
		needles = needles.split('');
	}	
    
	if( ! Array.isArray( needles )  ) {
		needles[0] = needles;
	}
	
	for( let element of needles ) {
		if( 'string' == typeof element ) {
			if( 1 < element.length ) {
				throw new Error('Error: String with length > 1 in array needles[]: ' + JSON.stringify( element ) ) ; 
			} else if( 1 == element.length ) {
				counter[element] = 0;
			} else {
				console.warn( 'Warníng: Empty string-element in array needles[] ' + JSON.stringify( element ) + ' ignored.' );
			}
		} else {
			throw new Error( 'Error: Element in array needles ( ' + JSON.stringify( element ) + ' ) is not of type string: It is a ' + typeof element + '.');
		}
	}	
	
	needles = Object.keys(counter);
	haystack.split('').forEach( 
		function( item, index, arr ) {
			for( let char of needles ) {
				if( item == char ) {
					counter[char]++;
				}
			}
		}
	);
	
	if( log ) {
		for( let char of needles.sort() ) {
			console.log( 
				'Char »' 
				+ ReplaceInvisibleChars( char )
				+ '« has ' 
				+ counter[char] 
				+ ' founds.'
			);
		}
	}
	return counter;
}

function ReplaceInvisibleChars( s ) {
	return s.replace( /\u0000/g, '[NULL]' )
		.replace( /\u0001/g, '[START OF HEADING]' )
		.replace( /\u0002/g, '[START OF TEXT]' )
		.replace( /\u0003/g, '[END OF TEXT]' )
		.replace( /\u0004/g, '[END OF TRANSMISSION]' )
		.replace( /\u0005/g, '[ENQUIRY]' )
		.replace( /\u0006/g, '[ACKNOWLEDGE]' )
		.replace( /\u0007/g, '[ALERT]' )
		.replace( /\u0008/g, '[BACKSPACE]' )
		.replace( /\u0009/g, '[HORIZONTAL TABULATION]' )
		.replace( /\u000A/g, '[LINE FEED]' )
		.replace( /\u000B/g, '[VERTICAL TABULATION]' )
		.replace( /\u000C/g, '[FORM FEED]' )
		.replace( /\u000D/g, '[CARRIAGE RETURN]' )
		.replace( /\u000E/g, '[SHIFT OUT]' )
		.replace( /\u000F/g, '[SHIFT IN]' )
		.replace( /\u0010/g, '[DATA LINK ESCAPE]' )
		.replace( /\u0011/g, '[DEVICE CONTROL 1]' )
		.replace( /\u0012/g, '[DEVICE CONTROL 2]' )
		.replace( /\u0013/g, '[DEVICE CONTROL 3]' )
		.replace( /\u0014/g, '[DEVICE CONTROL 4]' )
		.replace( /\u0015/g, '[NEGATIVE ACKNOWLEDGE]' )
		.replace( /\u0016/g, '[SYNCHRONOUS IDLE]' )
		.replace( /\u0017/g, '[END OF TRANSMISSION BLOCK]' )
		.replace( /\u0018/g, '[CANCEL]' )
		.replace( /\u0019/g, '[END OF MEDIUM]' )
		.replace( /\u001A/g, '[SUBSTITUTE]' )
		.replace( /\u001B/g, '[ESCAPE]' )
		.replace( /\u001C/g, '[FILE SEPARATOR]' )
		.replace( /\u001D/g, '[GROUP SEPARATOR]' )
		.replace( /\u001E/g, '[RECORD SEPARATOR]' )
		.replace( /\u001F/g, '[UNIT SEPARATOR]' )
		.replace( /\u0085/g, '[NEXT_LINE]' )
		.replace( /\u2028/g, '[LINE_SEPARATOR]' )
		.replace( /\u2029/g, '[PARAGRAPH_SEPARATOR' )
	;	
}



/*
 *  Tests:
*/

/*optional*/ var CharCounterLogDefault = true; // or false
var str = 'Test: ' + "\u0000\n" + "\x0A" + 'arg1.forEach(wert =>' + "\t" + '*(wert % 2 ? anzUngerade : anzGerade).push(korr005(wert));';

CharCounter( str );

//CharCounter( str, '()[]{}\'"', true );            /* Fine */
//CharCounter( str, [ "\n", "\t", '(', ')' ], true );          /* Fine */
//CharCounter( str, str , true );                   /* Fine: Count all chars in haystack  */
//console.log( CharCounter( str, str , false ) );   /* Fine: Count all chars in haystack, write nothing  */
//CharCounter( str, [' ',''] , true );              /* Fine with warning */
//CharCounter( str, null , true );                  /* Error */
//CharCounter( str, true , true );                  /* Error */
//CharCounter( str, ['(', null ] , true );          /* Error */
//CharCounter( str, [' ', 100.8 ], true );          /* Error */
//CharCounter( str, [ ' ', [ 'f','c' ] ], true );   /* Error */

Log-Output mit dem aktiviertem Test:

Char »[NULL]« has 1 founds.
Char »[HORIZONTAL TABULATION]« has 1 founds.
Char »[LINE FEED]« has 2 founds.
Char » « has 8 founds.
Char »%« has 1 founds.
Char »(« has 4 founds […]

Ich würde needles nicht false als Defaultparameter geben.

Ja. Das war ein Rest einer früheren Variante.

Dein Hantieren mit Counter und Needles finde ich ebenfalls unnötig kompliziert. Du erwartest für die needles einen String mit den zu prüfenden Zeichen, oder ein Array aus den zu prüfenden Zeichen. Werriwell.

  • Willichaberso!

Aber du machst es Dir dann äußerst schwer, das zu validieren. Mit verwirrenden Fehlermeldungen. Wenn ich Dir eine Zahl übergebe, maulst Du, dass an Position 0 im Array needles kein String stünde.

  • Leicht geändert.

Das Sortieren der Nadeln würde ich weglassen.

  • Geändert. Wird nur noch für die Debug-Ausgabe gemacht. Dort ist es nützlich, geb ich das Objekt (a.k.a. „hash“, „array with named keys“) zurück, dann ist war es nur ein Zeitfresser. Will der Verwender das sortieren: Das Rezept steht ja in der Funktion und einen Kopf hat er selbst…

Für die log-Schleife (deren Existenz ebenfalls auf dem DOT-Altar geopfert werden sollte),

Die ist fast der eigentliche Sinn der Sache … gelassen und schwer verbessert. Habe nur für die „Logausgabe“ eine kleine Universal-Funktion (ReplaceInvisibleChars()) zur lesbaren Ausgabe der „nicht druckbaren“ Zeichen hinzugefügt und benutzt. Nicht über die „gar nicht vorkommenden könnenden Zeichen“ wundern: nodejs könnte ja aus allerhand Quellen lesen…