DGL
https://delphigl.com/forum/

Spaltennamen wie in Excel
https://delphigl.com/forum/viewtopic.php?f=37&t=10298
Seite 1 von 1

Autor:  Frase [ Mi Feb 08, 2012 02:04 ]
Betreff des Beitrags:  Spaltennamen wie in Excel

Habe einen Vorschlag:

Schreibe eine Methode, die Spaltennamen (wie aus Excel, Calc, Numbers, etc. bekannt) zurückliefert. Also:

A, B, C, D, ..., Z, AA, AB, AC, AD, AE, ..., AZ, BA, BB, BC, ...

Die Methode nimmt einen Int, der die Spaltennummer darstellt. Wobei 0 = A, 1 = B, 2 = C, usw. und liefert den entsprechenden Spaltennamen als String zurück.

Mfg
~ Frase

Autor:  Aya [ Mi Feb 08, 2012 12:20 ]
Betreff des Beitrags:  Re: Design Challenge Vorschläge

Frase hat geschrieben:
Schreibe eine Methode, die Spaltennamen (wie aus Excel, Calc, Numbers, etc. bekannt) zurückliefert.

Ist das nicht etwas zu simpel?

Code:
  1. QString getColumnName(int index)
  2. {
  3.     QString result;
  4.     while (index >= 0) {
  5.         int c = (index % 26);
  6.         index = index / 26 - 1;
  7.  
  8.         result.insert(0, (char)(65 + c));
  9.     }
  10.     return result;
  11. }


Aya

Autor:  Lord Horazont [ Mi Feb 08, 2012 13:45 ]
Betreff des Beitrags:  Re: Spaltennamen wie in Excel

Rekursiv:
Code:
  1. ORD_A = ord("A")
  2. def colname_rec(i):
  3.     (d,m) = divmod(i, 26)
  4.     return (colname_rec(d-1) if d > 0 else "") + chr(m+ORD_A)


Iterativ:
Code:
  1. def colname_it(i):
  2.     d = i+1
  3.     s = ""
  4.     while d > 0:
  5.         (d,m) = divmod(d-1, 26)
  6.         s = chr(m+ORD_A) + s
  7.     return s


Als (effizienter) Generator:
Code:
  1. def colname_gen(start=0,count=None):
  2.     def inc(col):
  3.         for idx, ch in enumerate(col):
  4.             col[idx] = ch + 1
  5.             if ch < 25:
  6.                 return
  7.             else:
  8.                 col[idx] = 0
  9.         # if we end up here, we need to insert a new designator
  10.         col.append(0)
  11.    
  12.     col = list((ord(ch)-ORD_A for ch in colname(start)))
  13.     col.reverse()
  14.     if count is not None:
  15.         for i in range(start, start+(count-1)):
  16.             yield "".join((chr(ch+ORD_A) for ch in reversed(col)))
  17.             inc(col)
  18.         yield "".join((chr(ch+ORD_A) for ch in reversed(col)))
  19.     else:
  20.         while True:
  21.             yield "".join((chr(ch+ORD_A) for ch in reversed(col)))
  22.             inc(col)


Anwendung:
Code:
  1. Python 3.2.1 (default, Jul 11 2011, 18:54:42)
  2. [GCC 4.6.1 20110627 (Red Hat 4.6.1-1)] on linux2
  3. Type "help", "copyright", "credits" or "license" for more information.
  4. >>> from cols import *
  5. >>> colname_rek(0)
  6. 'A'
  7. >>> colname_rek(25)
  8. 'Z'
  9. >>> colname_rek(26)
  10. 'AA'
  11. >>> colname_it(0)
  12. 'A'
  13. >>> colname_it(25)
  14. 'Z'
  15. >>> colname_it(26)
  16. 'AA'
  17. >>> list(colname_gen(25, 10))
  18. ['Z', 'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI']
  19. >>> list(colname_gen(50, 10))
  20. ['AY', 'AZ', 'BA', 'BB', 'BC', 'BD', 'BE', 'BF', 'BG', 'BH']
  21. >>> list(colname_gen(700, 10))
  22. ['ZY', 'ZZ', 'AAA', 'AAB', 'AAC', 'AAD', 'AAE', 'AAF', 'AAG', 'AAH']


//edit: Generator-Code korrigiert
Wer sich übrigens fragt, wofür man einen unendlichen Generator braucht, dem sei der Ausdruck:
Code:
  1. cells = [["row {0}, col {1}".format(y, x) for y in range(10)] for x in range(10)]
  2. table = list(zip(*zip(colname_gen(), *zip(*cells))))
  3. for row in table:
  4.   print(" ".join("{0:15s}".format(col) for col in row))

ans Herz gelegt ;). Anstatt der List Comprehension für cells kann man auch wunderbar eine bestehendes 2D-Array mit erstem Index ≙ Spalte und zweitem Index ≙ Zeile nehmen. Wenn das Array transponiert ist, lässt man einfach den *zip()-Aufruf um *cells herum weg (also *zip(*cells) => *cells). Dank des unendlichen Generators ist der Ausdruck unabhängig von den Dimensionen des Arrays.

Autor:  Frase [ Mi Feb 08, 2012 19:40 ]
Betreff des Beitrags:  Re: Spaltennamen wie in Excel

Code:
  1. def colName(index: Int): String = index match {
  2.   case x if index < 26 => ('A' + x).toChar.toString
  3.   case _ => colName(index / 26 - 1) + colName(index % 26)
  4. }


Und in Verwendung:
Code:
  1. println((0 to 25).map(colName).mkString(", "))
  2. println((26 to 51).map(colName).mkString(", "))
  3. println((52 to 77).map(colName).mkString(", "))
  4. println((650 to 710).map(colName).mkString(", "))

liefert:
Code:
  1. A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
  2. AA, AB, AC, AD, AE, AF, AG, AH, AI, AJ, AK, AL, AM, AN, AO, AP, AQ, AR, AS, AT, AU, AV, AW, AX, AY, AZ
  3. BA, BB, BC, BD, BE, BF, BG, BH, BI, BJ, BK, BL, BM, BN, BO, BP, BQ, BR, BS, BT, BU, BV, BW, BX, BY, BZ
  4. YA, YB, YC, YD, YE, YF, YG, YH, YI, YJ, YK, YL, YM, YN, YO, YP, YQ, YR, YS, YT, YU, YV, YW, YX, YY, YZ, ZA, ZB, ZC, ZD, ZE, ZF, ZG, ZH, ZI, ZJ, ZK, ZL, ZM, ZN, ZO, ZP, ZQ, ZR, ZS, ZT, ZU, ZV, ZW, ZX, ZY, ZZ, AAA, AAB, AAC, AAD, AAE, AAF, AAG, AAH, AAI
  5.  


Featuring Pattern Matching und so.

@Horazont: Das Divmod sieht ja putzig aus :) Die Generatoren-Lösung ist alles andere als putzig. Die ist eher... strange. Klingt nach einem Konzept, was man sich mal zu Gemüte führen sollte.

@Aya: Yay, eine iterative C++ Lösung :) Muss man den QString nicht erst mit irgendwas initialisieren? Oder liegt der da jetzt schon so auf dem Stack herum? Der Aufruf an result.insert kommt mir etwas spanisch vor ansonsten.

Autor:  Lord Horazont [ Mi Feb 08, 2012 20:29 ]
Betreff des Beitrags:  Re: Spaltennamen wie in Excel

Frase hat geschrieben:
@Horazont: Das Divmod sieht ja putzig aus :) Die Generatoren-Lösung ist alles andere als putzig. Die ist eher... strange. Klingt nach einem Konzept, was man sich mal zu Gemüte führen sollte.

Nun, ein Generator ist nichts weiter als ein parametrisierter Iterator. Jedes yield gibt ein neues Element zurück (d.h. die Funktion wird dort bis zum nächsten aufruf von next() praktisch unterbrochen). Wenn nichts mehr rauskommt (return oder einfach ende der Funktion) wird implizit ne EndOfIteration Exception geworfen… Die Python-Schleifen wissen dann, das Schluss ist ;). Der Benutzer merkt davon nichts, wenn er nicht per Hand next() aufruft.

greetings

Autor:  yunharla [ Fr Feb 17, 2012 15:42 ]
Betreff des Beitrags:  Re: Spaltennamen wie in Excel

Zeit für ein bissl Shell :P
Code:
  1.  
  2. let rec xls index =
  3.     if(index >= 0) then (char (index % 26 + 65)).ToString() + xls(index/26-1)
  4.     else ""
  5.  
  6. printfn "Columns %A"(List.map xls [900 .. 970])
  7.  

Autor:  TAK2004 [ So Feb 19, 2012 15:41 ]
Betreff des Beitrags:  Re: Spaltennamen wie in Excel

Code:
  1. void GetColumnName(Int32 Index, String& Result)
  2. {
  3.     Int32 columnCount=0;
  4.     Int32 ind=Index;
  5.     while(ind>=0)
  6.     {
  7.         ind=ind/26-1;
  8.         ++columnCount;
  9.     }
  10.  
  11.     String result(columnCount);
  12.     char* ptr=const_cast<char*>(result.c_str());
  13.     for (Int32 i=columnCount-1;i>=0;--i)
  14.     {
  15.         ind=(Index%26);
  16.         Index=Index/26-1;
  17.         ptr[i]='A'+ind;
  18.     }
  19.     Result.Swap(result);
  20. }


Die Variante ist auf Performance ausgelegt, es wird zuerst die Anzahl der Spalten errechnet, um dann den benötigten Speicher zu allozieren und direkt auf einen Array zu arbeiten. Ich muss aus meiner Stringklasse den char* pointer ziehen, da der Array operator[] ein Funktionsaufruf ist und entsprechend overhead und unnötig mehr CPU aufwand produziert(im release build kann je nach compiler das natürlich inlined werden aber halt je nach compiler und nicht für alle).
Der erzeugte String wird dann mit dem übergebenen ausgetauscht(ist die Stringklasse aus Radon Framework, die arbeitet intern mit nen Array pointer).
Das Resultat wird als Referenz-Parameter zurück gegeben, damit nicht noch zwischenobjekte auf dem Stack erzeugt werden, wie bei der Funktionsrückgabe(C# z.B. würde Speicher für object erzeugen und dann den pointer hin und her kopieren aber bei C++ würde er die Klasse neu anlegen, copy constructor aufrufen und ne riesen sauerei produzieren).

CPU's sind immer schneller als der Speicher(je mobiler des so stärker der Unterschied) daher kann man unmengen an Berechnungen machen, bevor Speicherzugriffe günstiger sind und wenn man dann noch dynamische Speicheralloziierung im Code hat, dann sowieso.
Ich hab noch ein bisschen mit Caching experimentiert aber das macht hier kein Sinn, das es zu wenig Daten und zu einfache Berechnungen sind. Da reicht das CPU interne Caching und Predicted-Caching aus.

Ich hab beim experimentieren noch ein Bug in meiner Insert Methode, von der Stringklasse, gefunden und gefixt :)

Seite 1 von 1 Alle Zeiten sind UTC + 1 Stunde
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/