expertenaustausch > comp.lang.* > comp.lang.javascript

Wolfgang Wolf (26.09.2019, 11:44)
Hallo,

habe folgendes Problem: Möchte aus Daten eine csv-Datei erstellen. Das
funktioniert bereits. Leider sind in einigen Attributen Semikolons
enthalten, die natürlich die Ausgabe verstümmeln. Ich würde gerne vor
dem Export alle Semikolons durch ein anderes Zeichen ersetzen.

Nun könnte ich mit der Holzhammermethode vorgehen, also eine Schleife
über alle Array-Elemente und die Attribute jedes Objekts einzeln bearbeiten.

Geht das vielleicht auch komfortabler? So was wie: Alle Daten in einen
(großen) String serialisieren, eine globale Ersetzung vornehmen und aus
dem String wieder die Objekte und das Array herstellen? Ggf. nur die
Objekte einzeln serialisieren, bearbeiten und wiederherstellen?

Erwarte natürlich keine fertige Lösung, mir würden ein paar
"Verkehrsschilder" schon reichen.

Meine Daten sehen in etwa so aus:

data = [
{
artnr: "ACW0219-0.50-0",
benennung: "Draht;sw;0,5mm²;40°C...150°C",
?? bestand: "85,20"
},{
artnr: "ACW0219-1.50-0",
benennung: "Draht;sw;1,5mm²;40°C...150°C",
?? bestand: "100,10"
}
]

Die Ersetzungen müsste ich "nur" in bestimmten Feldern vornehmen, was
natürlich für die Holzhammermethode spricht. Andererseits wäre mir eine
universelle Funktion, bei der ich alle Felder anschaue, auch ganz recht,
damit ich dieses Ding nie wieder anfassen muss.

Danke euch für jeden Wink!

Schönen Gruß
W. Wolf
Wolfgang Wolf (26.09.2019, 13:40)
Am 26.09.2019 um 11:44 schrieb Wolfgang Wolf:
> Hallo,
> habe folgendes Problem: Möchte aus Daten eine csv-Datei erstellen. Das
> funktioniert bereits. Leider sind in einigen Attributen Semikolons
> enthalten, die natürlich die Ausgabe verstümmeln. Ich würde gerne vor
> dem Export alle Semikolons durch ein anderes Zeichen ersetzen.


Ok, habe das jetzt erst mal auf die Schnelle so gelöst:

for (var i = 0; i < data.length; i++) {
for (var prop in data[i]) {
var dProp = data[i][prop];
if (typeof dProp == "string") {
data[i][prop] = dProp.replace(/;/g,",");
}
}
}

Bessere Lösungen sind natürlich willkommen.

Gruß
W. Wolf
Claus Reibenstein (26.09.2019, 14:44)
Wolfgang Wolf schrieb am 26.09.2019 um 11:44:

> habe folgendes Problem: Möchte aus Daten eine csv-Datei erstellen. Das
> funktioniert bereits. Leider sind in einigen Attributen Semikolons
> enthalten, die natürlich die Ausgabe verstümmeln.


Aber nur dann, wenn Du Semikolon als Trennzeichen benutzt _und_ der
Exporter kaputt ist.

> Ich würde gerne vor
> dem Export alle Semikolons durch ein anderes Zeichen ersetzen.


Sinnvoller wäre es, den Exporter zu reparieren.

Alternativ könntest Du auch ein anderes Zeichen als Trenner verwenden.
Tipp: Das "C" in CSV steht für "Comma" ;-)

Gruß
Claus
Wolfgang Wolf (26.09.2019, 15:57)
Am 26.09.2019 um 14:44 schrieb Claus Reibenstein:

> Aber nur dann, wenn Du Semikolon als Trennzeichen benutzt _und_ der
> Exporter kaputt ist.


Der "Exporter" ist Teil eines Frameworks (SenchaExt). Da pfuscht man
nicht gerne drin rum. Bis auf diese "Kleinigkeit" tut's ja. So kann er
z.B. ganz vernünftig mit den Gänsefüßchen (das kommt bei uns immer
wieder mal vor, z.B. als Zeichen für Zoll) umgehen. Nur bei dem
Semikolon bockt er.

> Sinnvoller wäre es, den Exporter zu reparieren.
> Alternativ könntest Du auch ein anderes Zeichen als Trenner verwenden.
> Tipp: Das "C" in CSV steht für "Comma" ;-)


Warum? Im Semikolon befindet sich doch ein Komma ;-)

Nun ja, inzwischen tut's ja. Der faule Entwickler wollte das halt nur in
einem Einzeiler gelöst haben...

Gruß
W. Wolf
Thomas 'PointedEars' Lahn (26.09.2019, 18:00)
Wolfgang Wolf wrote:

> Am 26.09.2019 um 11:44 schrieb Wolfgang Wolf:
> Ok, habe das jetzt erst mal auf die Schnelle so gelöst:
> for (var i = 0; i < data.length; i++) {


Ausser wenn der Wert von ?data.length? sich ändern kann, solltest Du den
Wert nur ein einziges Mal abfragen.

for (var i = 0, len = data.length; i < len; i++) {

Rückwärts zu iterieren, wenn es auf die Reihenfolge nicht ankommt, ist in
der Regel effizienter, weil der alte Wert der Iterationsvariablen nicht
gespeichert werden muss:

for (var i = data.length; --i;) {

> for (var prop in data[i]) {


Wenn Du for..in-Schleifen verwendest, dann wird über alle aufzählbaren
Eigenschaften des Objekts und seiner Prototypen iteriert. Das solltest Du
vermeiden, denn Du weisst nie, welche Eigenschaften ein Objekt hat.

Über Array-Objekte wird modern so iteriert:

data.forEach(callback);

Über die nicht-vererbten aufzählbaren Eigenschaften eines Objekts so:

Object.keys(obj).forEach(callback);

> var dProp = data[i][prop];


Da ?callback? eine Funktion referenzieren muss, brauchst Du dann auf data[i]
nicht mehr separat zuzugreifen: der Wert wird als erstes Argument der
Funktion übergeben und ist somit über den ersten formalen Parameter
verfügbar.

> if (typeof dProp == "string") {


Korrekt. [Ich habe gerade herausgefunden, dass ?dProp instanceof String?
nicht (mehr) funktioniert. ?Object(dProp) instanceof String? ginge zwar,
aber was soll das? Jetzt kann man nicht (mehr) mit Polymorphie von String
ableiten :-( Ist ähnlich idiotisch wie dass ?this? bei Arrow Functions
nicht mehr gesetzt werden kann, so dass der Code unten mit *nur* Arrow
Functions _nicht_ funktioniert.]

Beachte, dass Duck Typing zwar hier auch möglich ist, aber unerwünschte
Ergebnisse haben kann, weil alle nativen Objekte die toString-Methode von
Object.prototype erben.

> data[i][prop] = dProp.replace(/;/g,",");


Sobald Du auf ein Objekt mehrfach zugreifst (hier: das von data[i]
referenzierte Objekt) lohnt es sich die Referenz in einer Variable zu
speichern und dann auf diese Variable zuzugreifen.

> }
> }
> }


Zusammenfassend:

data.forEach(element => {
Object.keys(element).forEach(function (key) {
let value = this[key];

if (typeof value === 'string')
{
this[key] = value.replace(/;/g, ',');
}
}, element);
});

Ein Einzeiler kann es also nicht werden, ausser Du magst Spaghetticode oder
verwendest eine Library.

Ob Du dabei ES 6-Syntax verwendest, ist natürlich Dir überlassen. Mit
Präprozessoren wie Babel.js gibt es aber ? ausser der Build-Prozess soll
nicht geändert werden ? keinen guten Grund mehr, sie nicht zu verwenden.

Bedenke ausserdem, dass es einen guten Grund gibt, weshalb dort Semikolons
und *nicht* Kommas stehen: In

"Draht;sw;0,5mm²;40°C...150°C"

sind die Daten eindeutig. Bei

"Draht,sw,0,5mm²,40°C...150°C"

(das Ergebnis Deiner Ersetzung) ist das nicht mehr der Fall (ist das Komma
dort ein Feldtrenner oder ist es ein Dezimalkomma?). Widrigenfalls müsstest
Du also zum Beispiel auch das Zahlenformat konvertieren, *bevor* Du den
Feldtrenner konvertierst ? sonst geht Information verloren.

> Bessere Lösungen sind natürlich willkommen.


[x] done
Thomas 'PointedEars' Lahn (26.09.2019, 18:09)
Thomas 'Ingrid' Lahn wrote:

> for (var i = data.length; --i;) {


Das iteriert natürlich nicht über i === 0 (kann aber dann auch hilfreich
sein).

Gemeint war:

for (var i = 0, len = data.length; i < len; --i++) {

> Beachte, dass Duck Typing zwar hier auch möglich ist, aber unerwünschte
> Ergebnisse haben kann, weil alle nativen Objekte die toString-Methode von
> Object.prototype erben.


s/nativen/eingebauten (built-in)/

Wobei ich da gerade nicht sicher bin, ob das noch für alle eingebauten
Objekte gilt oder nur die, die erstmals in ES 3 definiert wurden.
Wolfgang Wolf (28.09.2019, 10:22)
Am 26.09.2019 um 18:00 schrieb Thomas 'PointedEars' Lahn:

> Ausser wenn der Wert von ?data.length? sich ändern kann, solltest Du den
> Wert nur ein einziges Mal abfragen.
> for (var i = 0, len = data.length; i < len; i++) {


Das leuchtet ein und ist absolut sinnvoll, weil es nichts kostet.
Da wo es darauf ankommt, kann man so die letzte Millisekunde auch noch
einsparen. Auch ich verwende in manchen Programmteilen genau diese
Methodik. In der ersten Klasse lernt man es i.d.R. anders und das sind
so Dinge, bei denen man meistens bleibt. Sicherlich auch dadurch
bedingt, dass es oft keinen für den Anwender spürbaren Effekt hat. Schon
gar nicht hier in meinem Fall, wo es um einen Datenexport geht, bei dem
im Anschluss die csv zum Download angeboten wird.

> Rückwärts zu iterieren, wenn es auf die Reihenfolge nicht ankommt, ist in
> der Regel effizienter, weil der alte Wert der Iterationsvariablen nicht
> gespeichert werden muss:


Absolut d'accord!

> Wenn Du for..in-Schleifen verwendest, dann wird über alle aufzählbaren
> Eigenschaften des Objekts und seiner Prototypen iteriert. Das solltest Du
> vermeiden, denn Du weisst nie, welche Eigenschaften ein Objekt hat.

In diesem Fall schon. Die Daten kommen immer von der gleichen Funktion,
die diese aus einer Tabelle extrahiert. Das geht sogar soweit, dass ich
mir die String-Prüfeng auch noch sparen könnte. Habe das mal bei einer
größeren Datenmenge getestet uns es bringt gefühlt gar nichts, also habe
ich dieses Mindestmaß an Validierung drin gelassen.

> Über Array-Objekte wird modern so iteriert:
> data.forEach(callback);


Na ja, mit der Rückwärtsschleife haben wir die letzte ms raus geholt,
und nun verschenken wir sie vermutlich wieder. Vielleicht liege ich auch
falsch, aber ich kann mir nicht vorstellen, dass die Interpreter heute
in der Lage sind so was besser zu optimieren, wie eine handgemachte
Schleife - modern hin oder her.

Ehrlich, dieses Neumodische kotzt mich manchmal auch an. Hier werden
-zig neue Wege geschaffen, die letzten Endes zum gleichen Ergebnis
führen. Es gibt so ein altes Volkslied, bei dem sich immer die eine
Zeile wiederholt: "Aber schön muss sie sein..."

> Zusammenfassend:
> data.forEach(element => {
> Object.keys(element).forEach(function (key) {
> let value = this[key];
> if (typeof value === 'string')
> {
> this[key] = value.replace(/;/g, ',');
> }
> }, element);
> });


Und ist das jetzt schöner lesbar, als zwei ehrliche altmodische
Schleifen? Ist es schneller? Oder kompatibler? Moderner ja, aber sonst?

[...]
> "Draht;sw;0,5mm²;40°C...150°C"
> sind die Daten eindeutig. Bei
> "Draht,sw,0,5mm²,40°C...150°C"
> (das Ergebnis Deiner Ersetzung) ist das nicht mehr der Fall (ist das Komma
> dort ein Feldtrenner oder ist es ein Dezimalkomma?).


Damit, dass in der deutschen Sprache das Dezimaltrennzeichen das gleiche
ist wie das Nebensatztrennzeichen, damit muss unser Kulturraum leben,
was soll's... ;-)

Vielen Dank für deine (top) Anmerkungen und Ergänzungen.

Gruß
W. Wolf
Stefan Mayer (28.09.2019, 14:25)
Am Samstag, den 28.09.2019, 10:22 +0200 schrieb Wolfgang Wolf:

> [CSV Export]
> Damit, dass in der deutschen Sprache das Dezimaltrennzeichen das gleiche
> ist wie das Nebensatztrennzeichen, damit muss unser Kulturraum leben,
> was soll's... ;-)


Nach Millionen von erstellten und gelesenen Zeile hat sich für bei mirder Tab
als Feldtrenner und kein Wertebegrenzer sowie UTF-8 Zeichensatz als
praktikabelste Einstellung etabliert.

Wermutstropfen sind leider die vielen Nachfragen der Excelbenutzer :-)

> Vielen Dank für deine (top) Anmerkungen und Ergänzungen.


Da schließe mich an.

Schönen Tag noch.
ciao, Stefan
Thomas 'PointedEars' Lahn (28.09.2019, 17:41)
Wolfgang Wolf wrote:

> Am 26.09.2019 um 18:00 schrieb Thomas 'PointedEars' Lahn:
>> Wenn Du for..in-Schleifen verwendest, dann wird über alle aufzählbaren
>> Eigenschaften des Objekts und seiner Prototypen iteriert. Das solltest
>> Du vermeiden, denn Du weisst nie, welche Eigenschaften ein Objekt hat.

> In diesem Fall schon. Die Daten kommen immer von der gleichen Funktion,
> die diese aus einer Tabelle extrahiert.


Ausser Du kannst die Implementierungen eingrenzen, mit denen Dein Code
ausgeführt wird, weisst Du nie, welche aufzählbaren Eigenschaften ein Objekt
hat.

Siehe auch:

<http://PointedEars.de/es-matrix>

<https://github.com/airbnb/javascript>

In diesem Zusammenhang empfehle ich (aufgrund eigener, positiver Erfahrung)
die Verwendung von

<https://eslint.org/>

(Die Airbnb-Empfehlungen kann man sich zurechtbiegen, wenn es gar nicht
passen sollte. Man sollte das aber nur machen, wenn man weiss, was man
tut.)

> Das geht sogar soweit, dass ich mir die String-Prüfeng auch noch sparen
> könnte. Habe das mal bei einer größeren Datenmenge getestet uns es bringt
> gefühlt gar nichts, also habe ich dieses Mindestmaß an Validierung drin
> gelassen.


Wie gesagt erbt jedes eingebaute Objekt die .toString()-Methode von
Object.prototype(), so dass man damit (explizit oder implizit) jeden Wert
nach String konvertieren kann, um dann auf dem Ergebnis
String.prototype.replace() ausführen zu können. Das heisst aber nicht, dass
das Ergebnis auch sinnvoll ist oder dass das effizient ist. Die Prüfung auf
String ist also sinnvoll.

>> Über Array-Objekte wird modern so iteriert:
>> data.forEach(callback);

> Na ja, mit der Rückwärtsschleife haben wir die letzte ms raus geholt,
> und nun verschenken wir sie vermutlich wieder.


Nein.

> Vielleicht liege ich auch falsch,


Das tust Du insofern, als dass die Funktion nur einmal compiliert werden
muss. Inwiefern die mehrfachen Aufrufe dann mehr Laufzeit kosten, müsste
man mal ausprobieren (normalerweise sind mehrfache Funktionsaufrufe
ineffizienter, bei Callbacks kann das aber anders sein).

> aber ich kann mir nicht vorstellen, dass die Interpreter heute
> in der Lage sind so was besser zu optimieren, wie eine handgemachte
> Schleife - modern hin oder her.


Ich schon.

> Ehrlich, dieses Neumodische kotzt mich manchmal auch an. Hier werden
> -zig neue Wege geschaffen, die letzten Endes zum gleichen Ergebnis
> führen. Es gibt so ein altes Volkslied, bei dem sich immer die eine
> Zeile wiederholt: "Aber schön muss sie sein..."


Das ?Neumodische? (jetzt auch schon wieder 10 Jahre alt; ECMAScript Ed. 5
wurde 2009 veröffentlicht und 2010 mit Mozilla JavaScript 1.8.2 erstmals
implementiert) ist auf jeden Fall weniger fehlerträchtig. Sämtliche
schleifenbezogenen Variablen sind auch dann automatisch im Scope begrenzt,
wenn man nicht Block-Scoping verwendet (das gab es bei der Einführung dieser
Methoden noch nicht), nämlich dadurch dass sie lokal im Callback sind.

> Und ist das jetzt schöner lesbar, als zwei ehrliche altmodische
> Schleifen?


Ja. Da steht fast wörtlich genau, was gemacht werden soll, und man muss es
nicht erst aus der Schleifenlogik erraten.

> Ist es schneller?


Ob es *insgesamt* schneller ist, weiss ich nicht. Aber die nativ in C/C++
oder Java implementierten Schleifen der Script-Engines sind *sicher*
schneller als JIT-compilierte in ECMAScript.

> Oder kompatibler?


Nein, es gibt noch uralte Implementierungen mit < 1 % weltweitem
Marktanteil, die das nicht nativ unterstützen:

<http://pointedears.de/scripts/test/es-matrix/?filter=Array%5C.prototype%5C.forEach%7CObject%5C. keys>

Aber selbst dafür gibt es ja Babel.js & Co. oder man verwendet einen
Polyfill.

> [...]
> Damit, dass in der deutschen Sprache das Dezimaltrennzeichen das gleiche
> ist wie das Nebensatztrennzeichen, damit muss unser Kulturraum leben,
> was soll's... ;-)


Das ist keine Frage der Sprache, sondern landesspezifisch. In der
deutschsprachigen Schweiz zum Beispiel, wo ich seit geraumer Zeit lebe, wird
in der Regel mindestens bei Geldbeträgen der Punkt als Dezimaltrennzeichen
verwendet:

<https://www.bk.admin.ch/bk/de/home/dokumentation/sprachen/hilfsmittel-textredaktion/schreibweisungen.html>

Dort Abschnitt 5.1.3.

(Das war meine Referenz, als ich an <https://www.priminfo.admin.ch/>
mitarbeitete.)

Siehe auch: <https://de.wikipedia.org/wiki/Schreibweise_von_Zahlen>

Das Komma ist übrigens im Deutschen nicht ausschliesslich ein
?Nebensatztrennzeichen?:

<https://www.duden.de/sprachwissen/rechtschreibregeln>

Siehe dort u.a. D11, D32, und D100 bis D132.

> Vielen Dank für deine (top) Anmerkungen und Ergänzungen.


Bitte, danke.
Wolfgang Wolf (07.10.2019, 15:59)
Am 28.09.2019 um 14:25 schrieb Stefan Mayer:

> Wermutstropfen sind leider die vielen Nachfragen der Excelbenutzer :-)


Das ist leider kein Wermutstropfen, sondern eine ganze Badewanne voll
Tränen. Wie läuft es denn in der Praxis ab? Der Anwender klickt auf
"Export" oder ähnlich und der Browser bietet einen Dialog mit "Datei
speichern" oder "Öffnen mit...". Hinter "Öffnen mit..." verbirgt sich
i.d.R. (also bei meinen Anwendern ganz sicher) ein Excel, welches ohne
irgendwelche Sonder-Konfigurationen eine Semikolon-getrennte CSV
anstandslos ließt und darstellt. Wenn die Kodierung und das BOM auch
noch stimmen, werden sogar die Sonderzeichen korrekt dargestellt. Für
alles andere muss der Text-Import-Assistent herhalten, der zwar mit
allen unterschiedlichen Konfigurationen klar kommt, aber halt nicht so
komfortabel ist, wie konfigurationsloses Direktladen.

Schönen Gruß
W. Wolf
Wolfgang Wolf (07.10.2019, 16:06)
Am 28.09.2019 um 17:41 schrieb Thomas 'PointedEars' Lahn:
> Ausser Du kannst die Implementierungen eingrenzen, mit denen Dein Code
> ausgeführt wird...


Genau das meinte ich mit "immer von der gleichen Funktion". Diese
Funktion wird mit einem Template gefüttert. Dort enthalten sind bereits
die Semikolons als Feldtrennzeichen (hier könnte ich natürlich auch ein
anderes Zeichen verwenden, macht aber zusammen mit Excel nur bedingt
Sinn (s. Antwort an Stefan).

Läuft alles inzwischen ganz gut - bin happy! Danke!

Schönen Gruß
W. Wolf
Stefan Mayer (08.10.2019, 00:42)
Am Montag, den 07.10.2019, 15:59 +0200 schrieb Wolfgang Wolf:
[..]
> alles andere muss der Text-Import-Assistent herhalten, der zwar mit
> allen unterschiedlichen Konfigurationen klar kommt, aber halt nicht so
> komfortabel ist, wie konfigurationsloses Direktladen.


Sehr trefflich. Erstaunlich, dass das nicht besser funktioniert.

LibreOffice wenigstens bietet beim Öffnen eine passenden Dialog an undmerkt
sich die Einstellung. Immerhin, wenn es die Zielgruppe kennen würde.

ciao, Stefan
Thomas 'PointedEars' Lahn (10.10.2019, 01:26)
Wolfgang Wolf wrote:

> Am 28.09.2019 um 17:41 schrieb Thomas 'PointedEars' Lahn:
> Genau das meinte ich mit "immer von der gleichen Funktion". Diese
> Funktion wird mit einem Template gefüttert. Dort enthalten sind bereits
> die Semikolons als Feldtrennzeichen (hier könnte ich natürlich auch ein
> anderes Zeichen verwenden, macht aber zusammen mit Excel nur bedingt
> Sinn (s. Antwort an Stefan).


Anscheinend hast Du nicht verstanden, was mit ?Implementierung? gemeint ist.
Gemeint ist die Implementierung *von ECMAScript*; je nach Laufzeitumgebung,
z. B. Web-Browser, ist das eine *andere*. Siehe ECMAScript Support Matrix.
Ähnliche Themen