Performance verbessern

Programmierung unter AOO/LO (StarBasic, Python, Java, ...)

Moderator: Moderatoren

Gast

Performance verbessern

Beitrag von Gast » Sa, 08.07.2017 14:46

Ich würde gerne Verbesserungen an meinem Code vornehmen.

Ich möchte gerne ein Feld "Preis" in einer csv. Datei vergleichen. Dabei habe ich ein Feld "Artikelnummer" bei dem der Inhalt bei einigen Zeilen gleich sein kann. Sobald die Artikelnummer gleich ist, möchte ich die Zeile mit dem höheren Preis löschen. Ich laufe in einer Schleife über das ganze Sheet um die Artikelnummern zu überprüfen. Eine innere Schleife läuft pro Artikelnummer über das ganze Sheet.

Mein Code macht das was ich möchte, jedoch VIEL zu langsam.


Code: Alles auswählen

Sub Main

	'Zugriff zum Dokument
	Dim oDoc As Object
	oDoc = ThisComponent
	'Zugriff zum ersten Datenblatt
	Dim oSheet As Object
	oSheet = oDoc.Sheets(0)
	
	Dim Zeile As Integer: Zeile = 1
	
	'Zugriff zur ersten Artikelnummer
	Dim firstArtikelnummer As String
	firstArtikelnummer = oSheet.getCellByPosition(0,Zeile).getString
	
	'Zugriff zum ersten Nettopreis
	Dim firstNettopreis As String
	firstNettopreis = oSheet.getCellByPosition(5,Zeile).getString
	
	'Mit Schleife über alle Artikel gehen
	While first <> ""
		SucheArtikelnummer firstArtikelnummer, firstNettopreis, Zeile
		Zeile = Zeile + 1
		firstArtikelnummer = oSheet.getCellByPosition(0,Zeile).getString
		firstNettopreis = oSheet.getCellByPosition(5,Zeile).getString
	Wend




End Sub


'Suche eine Zeile mit gleicher Artikelnummer, vergleiche Preise und lösche den Teuersten
Private Sub SucheArtikelnummer(ArtikelSuche as String, NettopreisSuche as String, ZeileSuche as Integer)

	'Zeilen-Count
	Dim countZeile As Integer: countZeile = 1
	
	odoc=thiscomponent
 	mysheet=odoc.sheets(0)
 	myrows=mysheet.getrows
 	
	Dim InhaltArtikel As String
	InhaltArtikel = mySheet.getCellByPosition(0,countZeile).getString
	Dim InhaltPreis As String
	InhaltPreis = mySheet.getCellByPosition(5,countZeile).getString
	
	'Mit Schleife alle Artikel durchgehen
	While InhaltArtikel <> ""
		If ArtikelSuche = InhaltArtikel Then
			If NettopreisSuche < InhaltPreis AND NettopreisSuche >= 0 Then
				myrows.removebyindex(countZeile, 1)
			End If
			If NettopreisSuche > InhaltPreis AND NettopreisSuche >= 0 Then
				myrows.removebyindex(ZeileSuche, 1)
			End If
		End If	
		countZeile = countZeile + 1
		InhaltArtikel = mySheet.getCellByPosition(0,countZeile).getString
		InhaltPreis = mySheet.getCellByPosition(5,countZeile).getString
	Wend

End Sub
Dies ist mein erstes Makro. Hoffentlich kann mir hier jemand weiterhelfen (sofern ich mich verständlich ausgedrückt habe).

Gruß
akBenutzer

Stephan
********
Beiträge: 10169
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Re: Performance verbessern

Beitrag von Stephan » Sa, 08.07.2017 20:53

Ja, diese Langsamkeit bei Direktzugriff auf Tabellen ist bekannt. Um das zu beschleuinigen musst Du den gesamten zu bearbeitenden Bereich in ein Array einlesen, dann das Array bearbeiten und das bearbeitete Array zurückschreiben.

Nehmen wir an der zu bearbeitende Bereich wäre A1:F1000, dann:

Code: Alles auswählen

oDoc = ThisComponent
'Zugriff zum ersten Datenblatt
Dim oSheet As Object
oSheet = oDoc.Sheets(0)
	
meine_Daten = oSheet.getCellRangeByName("A1:F1000").getDataArray()
die Daten sind nun im Array "meine_Daten", aber dieses Array hat nur eine Spalte mir 1000 Unterarrays, also muss man es ggf. noch umformen:

Code: Alles auswählen

k = Ubound(meine_Daten())
Dim Daten_Normalform(k,5) 
For i = 0 To k
	tmp = meine_Daten(i)
	For j = 0 To 5
		Daten_Normalform(i,j) = tmp(j)
	Next j
Next i
Das Array "Daten_Normalform" hat nun die Struktur wie der DAtenbereich in der Tabelle und Du kannst mit diesem Array alle Bearbeitungen durchführen, danach dieses Array umwandeln und inn die Tabelle zurückschreiben mit

Code: Alles auswählen

oSheet.getCellRangeByName("A1:F1000").setDataArray(das_ErgebnisArray())

Ich hoffe das hilft Dir zunächst weiter, zumindest hat Du die richtigen 'Stichworte' 'getDataArray() und setDataArray() und kannst hier im Forum (oder im Internet) nach weiteren Infos suchen..



Gruß
Stephan

Benutzeravatar
balu
********
Beiträge: 3558
Registriert: Fr, 24.08.2007 00:28
Wohnort: Warstein

Re: Performance verbessern

Beitrag von balu » So, 09.07.2017 03:13

Hallo!

@Stephan
Sehe meine kommenden Einwende bitte nicht als einen persönlichen Angriff dir gegenüber an. Und sollte ich dich irgendwie falsch verstanden haben, so sag mir das bitte schön.

Also denn.
Stephan hat geschrieben: die Daten sind nun im Array "meine_Daten", aber dieses Array hat nur eine Spalte mir 1000 Unterarrays ...
Mag vielleicht sein das dies wohl der richtige Technische Ausdruck dafür ist, ich jedoch sehe das etwas anders was auch für die Verständigung für Anfänger leichter zu verdauen ist. Ich versuche das mal so weit wie möglich recht einfach zu beschreiben.

Durch die Aktion meine_Daten = oSheet.getCellRangeByName("A1:F1000").getDataArray() wird der Datenbereich in den Arbeitsspeicher eingelesen. Es wird so gesehen fast eine 1:1 Kopie des Zellbereichs A1:F1000 dort angelegt. Das kann man sich ja noch sehr gut und einfach merken.

Wenn man jetzt aber damit per Basic weiter arbeiten möchte, dann wird es jetzt etwas, ein klein wenig, komplizierter.

Fangen wir mit den Koordinaten an.
Eine Zelle im Tabellenblatt, nehmen wir die Zelle B2, wird immer erst mit der Spalte und dann mit der Zeile angesprochen.
B, ist ganz klar die Spalte.
2, ist dann natürlich die Zeile.

Und in der Programmierung sieht das dann wie folgt aus.

Code: Alles auswählen

getCellRangeByName("B2")
Aber mann kann die Zelle auch noch über die Position ansprechen.

Code: Alles auswählen

getCellByPosition(1,1)
Aber auch hier kommt erst die Spalte, und dann die Zeile.


So, und nun folgt ein Sprung zu der Calc-Funktion =SVERWEIS().
Wer SVERWEIS kennt, der weiss das erst in einer Spalte gesucht wird, und dann aus einer bestimmten Spalte rechts daneben mit der gleichen Zeilennummer ein Ergebnis zurück gegeben wird. Dürfte auch klar sein.

Aber auch hier wird erst mit der Spalte, und dann mit der Zeile gearbeitet. Bis hier hin gibt es also Parallelitäten zu der Zelladressierung.


Doch nun kommt der dicke und fette Sprung zum
ABER!
Um jetzt aber das mehrdimensionale Array, denn das ist ja der eingelesene Zellbereich A1:F1000 da er mehr als eine Spalte beinhaltet (eine Spalte wäre nur ein eindimensionales Array), muss jetzt eine relativ einfache Methode herhalten um daraus Daten auszulesen.
Stephan hat geschrieben: ... also muss man es ggf. noch umformen:
Nein, umformen muss man da nix. Man muss das mehrdimensionale Array nur anders ansprechen, um damit zu arbeiten können. Und das ist eigentlich so weit auch kein besonders großes "Hexenwerk". Mann muss sich jetzt nur Geistlich etwas umstellen. Denn das bisher gesagte: "Erst Spalte, dann Zeile." wird nun auf den Kopf gestellt.

Ich habe eine kleine einfache Beispieldatei angehängt, um das folgende sich selber mal anschauen zu können. Zu finden ist das in der Datei im *Modul1*.

Code: Alles auswählen

	Dim oDok as Object, oBlatt1 as Object
	DIM oData	
	oDok = ThisComponent
	oBlatt1 = oDok.Sheets.GetByName("Tabelle1")
	oData1 = oBlatt1.getCellRangeByName("A1:E100")
	
	oData = oData1.getDataArray()
Das dürfte soweit verständlich sein.
Variablen deklaration, Variablen defenition, und den Datenbereich des Zellbereichs A1:E100 in ein Array einlesen.


Doch das folgende ist schon interessanter.

Code: Alles auswählen

For iAs = 0 to 4
	for iAz = LBound(oData()) to UBound(oData())
		if oData(iAz)(iAs) = 430 then ' <------ Hier kann auch ein anderer Wert eingegeben werden,
									  ' der aber im Tabellenblatt vorhanden sein muss.
			print "Gefunden! Array Koordinate ist: Zeile " & iAz &" Spalte " & iAs
		end if
   	next iAz
   next iAs
Es soll der Wert 430 gesucht werden, der auch im Tabellenblatt vorhanden ist. Und wenn er gefunden wurde, wird anschließend die Zeilen und die Spaltennummer des mehrdimensionalem Array ausgegeben.

iAs bedeutet: indexArraySpalte
iAz bedeutet: indexArrayZeile

Die eigentliche Suche, und das vergleichen mit dem Suchwert (Suchbegriff), geschieht hiermit.

Code: Alles auswählen

if oData(iAz)(iAs) = 430 then
Hier wird jetzt auch ersichtlich, das erst die Zeile, und dann die Spalte in dem mehrdimensionalem Array angesprochen wird. Das ist also das, was ich eben noch mit auf den Kopf stellen meinte.
Und ja(!), das ist richtig so geschrieben. Es sind 2 Klammerpaare direkt hintereinander ohne einen Punkt, Komma, oder sonstwas.

Die Reihenfolge, Zeile dann Spalte, kann auch NICHT getauscht werden. Also so etwas

Code: Alles auswählen

if oData(iAs)(iAz) = 430 then
führt dann unweigerlich zu einer Fehlermeldung. Die Reihenfolge ist nun mal halt fest vorgegeben, genauso wie die Zelladressierung im Tabellenblatt B2, die kann man auch nicht einfach auf 2B ändern.

Die beiden Schleifen arbeiten wohl erst die Spalten und dann die Zeilen ab, jedoch hat das nix mit der Adressierung des Arrays zu tun welche ja mit oData(iAz)(iAs) vorgenommen wird.


Jetzt nehmen wir uns mal vor die =SVERWEIS() zu ersetzen. Und das machen wir folgt, was in der Datei im *Modul2* nachgesehen werden kann.

Code: Alles auswählen

	for iAz = LBound(oData()) to UBound(oData())
		if oData(iAz)(1) = 111 then
			print oData(iAz)(4)
		end if
   	next iAz
Diesmal setzen wir die Spalte in der gesucht werden soll auf den festen Wert 1.

Code: Alles auswählen

if oData(iAz)(1) = 111 then
Und wenn in dieser Spalte der gesuchte Wert gefunden wurde, soll dann in der gleichen Zeile aus der Spalte 4 der dementsprechende Wert zurückgegeben werden.

Code: Alles auswählen

print oData(iAz)(4)
Ich hoffe das diese kleine Erklärung etwas hilfreich ist.
Natürlich ist dadurch das Hauptproblem von akBenutzer noch nicht gelöst, aber es ist schon mal der erste Schritt.

Ich mache jetzt aber Feierabend, es ist schon sehr Spät, oder zu früh. 8)



Gruß
balu
Dateianhänge
MAKRO_Einfaches_mehrdimensionales Array_0.ods
(14.35 KiB) 30-mal heruntergeladen
Sei öfter mal ein Faultier, sag öfter mal "Ach was!" Dann kriegst du keinen Herzinfarkt, und hast ne menge Spass.

wehr rächtschraipfähler findet khan si behalden :D

Stephan
********
Beiträge: 10169
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Re: Performance verbessern

Beitrag von Stephan » So, 09.07.2017 12:22

Code: Alles auswählen

if oData(iAz)(iAs) = 430 then
Das finde ich sehr interessant und habe ich so noch nie gesehen. Zweifelsfrei ist das besser (weil einfacher) als mein Vorgehen.
Die Schreibweise ist mir verständlich wenn ich sie sehe, nur wäre ich nicht auf die Idee gekommen das das so zu verwenden ist.

Kannst Du Deine Erkenntnis vielleicht bitte in einem Beitrag in:
viewtopic.php?f=27&t=54235

niederlegen? Vorzugsweise indem Du mein Vorgehen (Umformen) und Deines (direkter Zugriff) gegenüberstellst?

Ich fände das wichtig, weil mir Deine Version noch nirgends im Internet untergekommen ist und ich sie für eine große Erleichterung halte die man kennen sollte.


einzig:
Ob das für Anfänger leichter verständlich ist, weiß ich aber nicht, denn die Verwechselungsgefahr mit

Code: Alles auswählen

oData(iAz, iAs)
ist groß. Du selbst schreibst leider fälschlich:
Man muss das mehrdimensionale Array nur anders ansprechen, um damit zu arbeiten können.
bloß da ist kein eigentliches mehrdimensionales Array, denn dabei müsste die Größe in der zweiten Dimension abfragbar sein:

Code: Alles auswählen

Msgbox UBOUND(oData(),2)
Ich nenne deshalb solche Arrays immer "Array-in-Array", aber vielleicht kennt jemand einen besseren Begriff?



Gruß
Stephan

mikeleb
******
Beiträge: 637
Registriert: Fr, 09.12.2011 16:50

Re: Performance verbessern

Beitrag von mikeleb » So, 09.07.2017 12:56

Hallo,
die Funktionen getDataArray, setDataArray (und auch getFormulaArray, setFormulaArray) sind schon klasse, wenn auch nicht ganz einfach zu verstehen. Mir hat beim Lernen Thomas Krumbein "Makros in OpenOffice.org 3" unheimlich geholfen, aus dem ich kurz zu diesem Thema zitieren möchte:
"Es wird kein zweidiensionales Array erzeugt! Auch wenn es den Anschein hat."
"Es entsteht ein verschachteltes Array, wobei die Zeilen die erste Dimension des Arrays darstellen." Hier möchte ich anmerken, dass es de facto auch nur ein eindimensionales Array ist, denn eine zweite Dimension existiert nicht.
"Jedes dieser Elemente enthält wiederum ein Array, dass die Spalten der jeweiligen Zeile repräsentiert (und damit die Zellen)"
Wird also ein Zellbereich A1:C10 ausgelesen:

Code: Alles auswählen

 oBereich=ThisComponent.Sheets(0).getCellrangeByName("A1:C10")
aDat=oBereich.getDataArray
so erhalten wir mir aDat ein Array, dass aus 10 Elementen besteht (den Zeilen des ursprünglichen Bereiches entsprechend). Auf diese Elemente kann per aDat(i) (i kann die Werte 0 bis 9 annehmen; die Indexzählung beginnt bei Arrays ja üblicherweise bei 0).
Jedes dieser 10 Elemente ist nun selbst wieder ein Array mit 3 Elementen (entsprechend den Spalten A-C) auf das per aDat(i)(k) (k von 0 bis 2) zugegriffen werden kann.
aDat(5)(2) stellt also den Zugriff auf die 6. Zeile und 3. Spalte her, also zu dem Wert in Zelle C6.
Die Verwirrung zu dem sonst üblichen Zugriff: erst Spalte, dann Zeile bleibt natürlich.
Nach meinen ersten, stolpernden Gehversuchen in Python sei noch erwähnt, dass unter Python getDataArray und setDataArray mit Tupeln (nicht Listen!) arbeiten - es hatte mich einige verzweifelte Stunden gekostet ...
Gruß,
mikeleb

Stephan
********
Beiträge: 10169
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Re: Performance verbessern

Beitrag von Stephan » So, 09.07.2017 13:39

hier ist ein Bild zu Visualisierung, vielleicht ist es so für Lernende besser zu verstehen.


getDataArray.gif
getDataArray.gif (8.34 KiB) 1823 mal betrachtet

Benutzeravatar
balu
********
Beiträge: 3558
Registriert: Fr, 24.08.2007 00:28
Wohnort: Warstein

Re: Performance verbessern

Beitrag von balu » So, 09.07.2017 14:26

Hallo Stephan,
Ob das für Anfänger leichter verständlich ist, weiß ich aber nicht
Ich rede mich jetzt da mal etwas raus, weil es schon sehr Früh am morgen war. (eine freche Ausrede also :lol:)
Denn ich hätte in dieser Passage
balu hat geschrieben: Wenn man jetzt aber damit per Basic weiter arbeiten möchte, dann wird es jetzt etwas, ein klein wenig, komplizierter.
deutlicher hervorheben sollen das nun Schluß ist mit "leichter verständlich". Oder anders ausgedrückt: Schluß mit Lustig.

Du selbst schreibst leider fälschlich:
Man muss das mehrdimensionale Array nur anders ansprechen, um damit zu arbeiten können.
bloß da ist kein eigentliches mehrdimensionales Array
"Eigentlich" hast Du nicht ganz unrecht. Aber für mich als Anfänger bezüglich Arrays hatte ich echt so meine Probleme zu verstehen was ein Array wirklich ist, und wie man damit arbeiten kann. Da ich bis zu den Anfängen meiner Makro-Programmierung mehr in Calc "zu Hause war", tat ich mich damit halt schwer. Bis das es eines Tages bei mir "Klick" gemacht hatte. Denn dann erkannte ich eine gewisse Parallelität zu einem Zellbereich in einem Tabellenblatt.

Zellbereich A1:A100 ist ein eindimensionales Array. Da nur die Spalte A angesprochen wird.
Zellbereich A1:E100 ist ein mehrdimensinales Array. Da jetzt 5 Spalten, von A bis E angesprochen werden.

Und diese "Eselsbrücke" hat mir ungemein geholfen. Aber nicht nur die, sondern auch noch eine andere Quelle tar ihr übriges dazu (dazu gleich noch etwas).

Ich nenne deshalb solche Arrays immer "Array-in-Array", aber vielleicht kennt jemand einen besseren Begriff?
Korrekt! Meine Quelle bezeichnet so etwas auch als "Array-in-Array". Jedoch das widerum wirklich und wahrhaftig zu Begreifen und zu verstehen, ist ein Thema für sich selbst. Allein schon die Bezeichnung "Array-in-Array" ist irgendwie surreal, so seh ich das zumindest.

Nun, den vollen Umfang von Array zu Begreifen und zu verstehen, und was man wie alles damit anfangen kann, ist schon eine nicht grad leichte Kost. Und stellenweise kämpfe ich noch immer damit anständig rum. Das versteht man nicht von heut auf morgen.

Meine Quelle ist der hier wohl am meist zitierte Name überhaupt: Andrew Pitonyak
Und in der deutschen Version von "OpenOffice.org Macros Explained" lohnt es sich durchaus öfters mal reinzuschauen. Auch wenn man nicht gleich alles versteht, und stellenweise die Codelistings gewöhnungsbedürtig sind, so hat er mir in manchen Dingen schon öfters geholfen.

In der deutschen Version, Stand 13.05.2016, kann man dort auf der Blattseite 113 folgendes nachlesen.
deutschen Version hat geschrieben: Die Konstruktion „Array in Array“ ist manchmal nützlich, wenn es in offensichtlicher Beziehung zu der realen Datenorganisation steht.
Und für das einlesen eines Zellbereichs mittels getDataArray trifft ja darauf zu.
Zu finden nach "Listing 95.", welches das hier ist

Code: Alles auswählen

Sub ArrayInArray
  Dim v() : v = Array(Array(1, 2, 3), Array("eins", "zwei", "drei"))
  Print v(0)(1)
End Sub
auf der genannten Seite.
Das finde ich sehr interessant und habe ich so noch nie gesehen.
Und damit komme ich jetzt auch zur Beantwortung deiner Feststellung.
Als ich sah, und nach etlichen Versuchen verstand was bei

Code: Alles auswählen

Print v(0)(1)
geschieht, dachte ich mir: "Wenn Print etwas ausgibt, dann müsste ich doch mittels einer Schleife dieses Array auch abarbeiten können."
Und nach einigen Experimenten kamm ich dann zu diesem Ergebnis.

Code: Alles auswählen

if oData(iAz)(iAs) = 430 then

Ich werde mal schauen wann ich deiner Bitte nach kommen kann, in den FAQs etwas dazu zu schreiben. das kann aber einige Tage in anspruch nehmen, vielleicht auch länger.

Da ich ja nicht der schnellste im schreiben bin, und ihr zwischenzeitlich geantwortet habt, komme ich für heute auch zum Schluß.

Eins noch. Vielleicht sollte ich "Thomas Krumbein" auch noch auf meine "Haben-wollen-liste" setzen. ;-)



Gruß
balu
Sei öfter mal ein Faultier, sag öfter mal "Ach was!" Dann kriegst du keinen Herzinfarkt, und hast ne menge Spass.

wehr rächtschraipfähler findet khan si behalden :D

StePfl
**
Beiträge: 45
Registriert: Mo, 04.07.2016 17:16

Re: Performance verbessern

Beitrag von StePfl » Fr, 28.07.2017 19:53

mikeleb hat geschrieben:
So, 09.07.2017 12:56
die Funktionen getDataArray, setDataArray (und auch getFormulaArray, setFormulaArray) sind schon klasse,...
STIMMT - und getFormlaArray, setFormulaArray kannte ich bisher noch nicht - wo finde ich dazu eine Dokumentation?

Und das Problem, wie man die Dimension des Datenbereichs elegant ( ohne Schleife oder Überdimensionierung des einzlesenden Ranges) ermitteln kann, habe ich SO gelöst:
1. Die ein CellObjekt oder rangeObjekt im Datenbereich ermitteln
und folgende Funktion aufrufen:

Code: Alles auswählen

global Function GetRangeOfClosedRange(optional oStartCell) as object
Dim hlp
Dim locSheet, locCurs
	if ismissing(oStartCell) then	
		locCell = ThisComponent.CurrentSelection
		
	else
		locCell = oStartCell
	endif
	hlp = locCell.AbsoluteName
	locSheet = ThisComponent.sheets(locCell.RangeAddress.sheet)
	GetRangeOfUserdAerea = locCell

	locCurs = locSheet.createCursorByRange(locCell)
	locCurs.collapseToCurrentRegion()
	GetRangeOfClosedRange = locCurs
end function
und schon hat man ein RangeObjekt des gesamten, zusammenhängenden Datenbereichs.
Jedenfalls hat es bei mir prima geholfen.

Gruß,
StePfl

Edit: hatte die falsche Funktion kopiert - jetzt stimmts
Zuletzt geändert von StePfl am Fr, 28.07.2017 20:10, insgesamt 1-mal geändert.

Stephan
********
Beiträge: 10169
Registriert: Mi, 30.06.2004 19:36
Wohnort: nahe Berlin

Re: Performance verbessern

Beitrag von Stephan » Fr, 28.07.2017 20:00

STIMMT - und getFormlaArray, setFormulaArray kannte ich bisher noch nicht - wo finde ich dazu eine Dokumentation?
in der API-Dokumentation, siehe:
http://www.openoffice.org/api/docs/comm ... rmula.html


Gruß
Stephan

StePfl
**
Beiträge: 45
Registriert: Mo, 04.07.2016 17:16

Re: Performance verbessern

Beitrag von StePfl » Fr, 28.07.2017 20:01

und wieder ein WOW - bis du schnell!!!!!
Danke!

Antworten

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 13 Gäste