Suddivisione nota testuale in tabella

alfrimpa

VBA Expert
Original poster
Supermoderatore
18 Dicembre 2015
78.861
8.653
2.445
72
Napoli
Office 365
Buongiorno ges @ges e a tutti

Con riferimento a questa discussione


ho provato a chiedere (allegando il file) di scrivere una macro che desse il risultato desiderato.

Dopo qualche tentativo andato a vuoto (e mi sembra normale vista la complessità della questione) mi ha dato questo codice che mi pare funzioni correttamente

Visual Basic:
Sub CreaTabella()
    Dim ws As Worksheet
    Dim OutRow As Long
    Dim LastRow As Long
    Dim R As Long
    Dim Cod As String
    Dim Des As String
    Dim Nota As String
    Dim Lotto As String
    Set ws = ActiveSheet
    ws.Range("E1:H100000").ClearContents
    ws.Range("E1") = "CODICE"
    ws.Range("F1") = "DESCRIZIONE"
    ws.Range("G1") = "LUNGHEZZA"
    ws.Range("H1") = "LOTTO"
    OutRow = 2
    LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    For R = 2 To LastRow
        Cod = ws.Cells(R, 1).Value
        Des = ws.Cells(R, 2).Value
        Nota = LCase(ws.Cells(R, 3).Value)
        Lotto = ""
        Call EstraiLotto(Nota, Lotto)
        Nota = Replace(Nota, "<->", "+")
        Nota = Replace(Nota, "-", "+")
        Nota = Replace(Nota, "l=", "")
        Nota = Replace(Nota, "mm", "")
        Nota = Replace(Nota, "  ", " ")
        Call AnalizzaNota(Nota, Cod, Des, Lotto, ws, OutRow)
    Next
    MsgBox "Fine"
End Sub

Private Sub AnalizzaNota(Testo As String, Cod As String, Des As String, Lotto As String, _
                         ws As Worksheet, ByRef OutRow As Long)
    Dim T() As String
    Dim X As Variant
    Dim S As String
    Dim P As Long
    Dim Qta As Long
    Dim Valore As Long
    Dim I As Long
    Testo = Replace(Testo, "+", " + ")
    T = Split(Testo, " ")
    For I = LBound(T) To UBound(T)
        S = Trim(T(I))
        If S = "" Then GoTo Continua
        If InStr(S, "/") > 0 Then GoTo Continua
        If Left(S, 2) = "l." Then GoTo Continua
        If InStr(S, "pz") > 0 Then
            P = InStr(S, "pz")
            Qta = Val(Left(S, P - 1))
            If Len(Mid(S, P + 2)) > 0 Then
                Valore = Val(Mid(S, P + 2))
            Else
                I = I + 1
                Do While I <= UBound(T)
                    If IsNumeric(T(I)) Then
                        Valore = CLng(T(I))
                        Exit Do
                    End If
                    I = I + 1
                Loop
            End If
            Dim K As Long
            For K = 1 To Qta
                ws.Cells(OutRow, 5) = Cod
                ws.Cells(OutRow, 6) = Des
                ws.Cells(OutRow, 7) = Valore
                If Lotto <> "" Then
                    ws.Cells(OutRow, 8) = Lotto
                End If
                OutRow = OutRow + 1
            Next K
        Else
            If IsNumeric(S) Then
                Valore = CLng(S)
                If Valore > 0 Then
                    ws.Cells(OutRow, 5) = Cod
                    ws.Cells(OutRow, 6) = Des
                    ws.Cells(OutRow, 7) = Valore
                    If Lotto <> "" Then
                        ws.Cells(OutRow, 8) = Lotto
                    End If
                    OutRow = OutRow + 1
                End If
            End If
        End If
Continua:
    Next I
End Sub

Private Sub EstraiLotto(ByRef Testo As String, ByRef Lotto As String)
    Dim P1 As Long
    Dim P2 As Long
    P1 = InStr(Testo, "(l.")
    If P1 = 0 Then Exit Sub
    P2 = InStr(P1, Testo, ")")
    If P2 = 0 Then Exit Sub
    Lotto = Mid(Testo, P1 + 3, P2 - P1 - 3)
    Testo = Left(Testo, P1 - 1) & Mid(Testo, P2 + 1)
End Sub

A parte un banalissimo errore in riga 12 (aveva scritto ws.Range("E:H100000").ClearContents e quindi andava in debug altri errori non ce ne sono stati.

Cosa ne pensate?

Personalmente non sono in grado di dire se vi siano ridondanze o "errori" che non impediscono il corretto funzionamento del codice.
 

ges

Excel/VBA Expert
Amministratore
21 Giugno 2015
31.405
3.233
2.465
Como
2011Mac-365Win
Ciao Alfredo, mi sembra che c'è qualche passaggio di troppo, forse si può migliorare così;
Visual Basic:
Option Explicit
Sub CreaTabella()
    Dim ws As Worksheet, t() As String, s As String
    Dim r As Long, ultimaRiga As Long, outRow As Long
    Dim i As Long, k As Long, p As Long, p2 As Long
    Dim cod As String, des As String, nota As String, lotto As String
    Dim qta As Long, valore As Long
    Set ws = ActiveSheet
    Application.ScreenUpdating = False
    ws.Columns("E:H").ClearContents
    ws.Range("E1:H1").Value = Array("CODICE", "DESCRIZIONE", "LUNGHEZZA", "LOTTO")
    outRow = 2
    ultimaRiga = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
    For r = 2 To ultimaRiga
        cod = ws.Cells(r, 1).Value
        des = ws.Cells(r, 2).Value
        nota = LCase$(ws.Cells(r, 3).Value)
        lotto = ""
        p = InStr(nota, "(l.")
        If p > 0 Then
            p2 = InStr(p, nota, ")")
            If p2 > 0 Then
                lotto = Mid$(nota, p + 3, p2 - p - 3)
                nota = Left$(nota, p - 1) & Mid$(nota, p2 + 1)
            End If
        End If
        nota = Replace(Replace(Replace(Replace(nota, "<->", "+"), "-", "+"), "l=", ""), "mm", "")
        t = Split(Replace(nota, "+", " + "), " ")
        For i = LBound(t) To UBound(t)
            s = Trim$(t(i))
            If Len(s) > 0 And InStr(s, "/") = 0 And Left$(s, 2) <> "l." Then
                p = InStr(s, "pz")
                If p > 0 Then
                    qta = Val(Left$(s, p - 1))
                    If Len(Mid$(s, p + 2)) > 0 Then
                        valore = Val(Mid$(s, p + 2))
                    Else
                        valore = 0
                        Do While i < UBound(t)
                            i = i + 1
                            If IsNumeric(t(i)) Then valore = CLng(t(i)): Exit Do
                        Loop
                    End If
                    For k = 1 To qta
                        ws.Cells(outRow, 5).Resize(1, 3).Value = Array(cod, des, valore)
                        If Len(lotto) > 0 Then ws.Cells(outRow, 8).Value = lotto
                        outRow = outRow + 1
                    Next k
                ElseIf IsNumeric(s) Then
                    valore = CLng(s)
                    If valore > 0 Then
                        ws.Cells(outRow, 5).Resize(1, 3).Value = Array(cod, des, valore)
                        If Len(lotto) > 0 Then ws.Cells(outRow, 8).Value = lotto
                        outRow = outRow + 1
                    End If
                End If
            End If
        Next i
    Next r
    MsgBox "Fine", vbInformation
Uscita:
    Application.ScreenUpdating = True
End Sub
 

alfrimpa

VBA Expert
Original poster
Supermoderatore
18 Dicembre 2015
78.861
8.653
2.445
72
Napoli
Office 365
mi sembra che c'è qualche passaggio di troppo, forse si può migliorare così;
Si certo ero sicuro che fosse migliorabile ma io non sono ai tuoi livelli :studia: Muoio_muoio e scusa se mi sono accodato alla tua precedente discussione.

Grazie per averle separate.
 

Terio

Excel/Vba Expert
Supermoderatore
6 Gennaio 2021
28.795
6.317
2.345
55
Arce
2016, 2019, 365
ero sicuro che fosse migliorabile
Normalmente è quasi tutto migliorabile, compreso il fatto che, riferendosi ad una tabella, sarebbe stato opportuno usare l'oggetto specifico e non un ciclo all'ultima riga, ma non cambia il senso di quel che è riuscita a fare; a tal proposito manca quale AI hai utilizzato, per dovere di cronaca.
Ho provato con Copilot a fargli creare una formula in sostituzione della mia (che non gli ho fornito) e questo il risultato:
=STACK.VERT({"CODICE"\"DESCRIZIONE"\"LUNGHEZZA"};LET(c;Tabella2[CODICE];d;Tabella2[DESCRIZIONE];n;Tabella2[NOTA];ESCLUDI(REDUCE("";SEQUENZA(RIGHE(c));LAMBDA(acc;i;LET(cod;INDICE(c;i);des;INDICE(d;i);nota;MINUSC(INDICE(n;i));txt;MINUSC(REGEX.SOSTITUISCI(INDICE(n;i);"\b\d{1,2}/\d{1,2}/\d{2,4}\b";""));txa;SOSTITUISCI(REGEX.SOSTITUISCI(txt;"\(l\.?\s*\d+\)";"");"<->";" ");txb;REGEX.SOSTITUISCI(txa;"(\d+)\s*pz\s*l\s*=*\s*(\d+)";"$1x$2");txc;REGEX.SOSTITUISCI(txb;"(\d+)\s*pz\s+(\d+)";"$1x$2");txd;REGEX.SOSTITUISCI(txc;"\d+\s*pz\s*"; "");txe;SOSTITUISCI(SOSTITUISCI(txd;"mm";"");"l=";"");arr;A.COL(REGEX.ESTRAI(txe;"\d+x\d+|\d+";1);1);q;SE.ERRORE(--TESTO.PRECEDENTE(arr;"x");1);lun;--TESTO.SUCCESSIVO(arr;"x";;;;arr);cum;SCAN(0;q;LAMBDA(a;b;a+b));rep;INDICE(lun;CONFRONTA.X(SEQUENZA(SOMMA(q));cum;1));righe;STACK.ORIZ(MAKEARRAY(RIGHE(rep);1;LAMBDA(r;col;cod));MAKEARRAY(RIGHE(rep);1;LAMBDA(r;col;des));rep);STACK.VERT(acc;righe))));1)))
tranne la diversa metodologia per creare il risultato finale, i passaggi sono molto simili concettualmente ai miei, premesso che il tempo di elaborazione non può assolutamente essere messo a paragone con il mio (enormemente inferiore) dimostra che anche l'approccio a soluzioni complesse è pregevole, ma mi avrebbe tolto tutta l'inventiva che ho avuto nel crearla e nel poter, eventualmente, sfruttare l'intuizione anche in altre occasioni.
Questo vuol dire che, secondo me, aiuta molto, ma rende legati, in prospettiva futura, alla disponibilità dell'AI.

Ciao.
N.B.
I riferimenti strutturati alla tabella li ho dovuti aggiungere, tendenzialmente sembra prediligere, come per il VBA, gli intervalli.

N.B. 2
Gemini e Claude vanno in confusione con la sintassi italiana e confermo che il concetto di fondo sembra simile, buona l'intuizione di usare uno SCEGLI({1\2\3} per espandere tentata da Gemini che costituisce, ad esempio, uno spunto di riflessione da replicare.
 

ClaudioFu

Utente junior
15 Novembre 2024
53
19
8
2024
Grazie alfrimpa @alfrimpa uso la AI quando ho un'idea e voglio mi aiuti a svilupparla. Partendo dalla premessa di non avere idee ho preferito la RI (intelligenza reale) che ha prodotto un'ottimo risultato grazie a Terio @Terio . Fra l'altro provando la macro in "produzione" sul foglio reale con circa 500 articoli i tempi sono biblici e gli "errori" di interpretazione del testo sono parecchi. Devo dire che al contrario la formula di Terio @Terio ha molti meno "inconvenienti" ed è istantanea nel produrre il risultato. Questo mi permette anche di "correggere" il testo e vederne immediatamente l'interpretazione.
Ovviamente è mio parere personale ma mi sembrava dovuto il feedback a chi ha speso impegno a risolvere il problema.
Un grazie anche a ges @ges per le correzioni.
Buona giornata a tutti
 
  • Like
Reactions: Terio

Terio

Excel/Vba Expert
Supermoderatore
6 Gennaio 2021
28.795
6.317
2.345
55
Arce
2016, 2019, 365
ha prodotto un'ottimo risultato
Grazie del complimento, anche interrogando l'AI è sempre importante come si chiedono le cose, era infatti partita dal solo esempio considerando come statico, tutto il testo da eliminare (ad esempio considerando anche scansia come testo da eliminare), io ho cercato di rendere quanto più dinamico il tutto, forse perché conosco meglio come sbaglia chi inserisce i dati :LOL:

Un saluto a tutti.
 

Robyprog

Utente abituale
11 Agosto 2019
751
254
65
Palermo
Excel 2010
Salve a tutti Saluto_saluto

Ho trovato questa discussione davvero interessante e ho voluto fare un esperimento un po' diverso.

Invece di chiedere a ChatGPT di risolvere il problema, gli ho fornito le due formule e il codice VBA chiedendogli di analizzarli, come farebbe qualcuno che sta cercando di capire il ragionamento che c'è dietro.

La cosa che mi ha colpito è che non si è limitato a descrivere le istruzioni una per una, ma ha individuato tre approcci progettuali differenti.

  • La prima (AI Copilot interrogata da Terio @Terio ) formula è stata interpretata come un algoritmo "esplicito": ogni passaggio (pulizia del testo, estrazione dei dati, espansione delle quantità e costruzione della tabella) è ben riconoscibile.
  • La seconda (By Terio @Terio RI) è stata vista come una versione più "funzionale": meno strutture intermedie, una pipeline di trasformazioni più compatta e un maggiore riutilizzo delle matrici dinamiche.
  • Del VBA (Riportata da alfrimpa @alfrimpa e revisionata da ges @ges ) , invece, ha evidenziato soprattutto la diversa filosofia: codice più lineare e facilmente modificabile, ma anche alcune differenze rispetto alle formule, ad esempio il fatto che l'interpretazione della nota dipende maggiormente dal formato del testo e che le scritture ripetute sul foglio potrebbero essere sostituite da un'elaborazione in memoria per migliorare le prestazioni.
La riflessione finale, però, è stata quella che mi è piaciuta di più.

Mi ha fatto notare che il confronto non dovrebbe essere "formula contro VBA" o "uomo contro AI", perché in realtà vengono ottimizzate caratteristiche diverse.

Una formula può privilegiare compattezza ed eleganza, un'altra la leggibilità del flusso logico, mentre il VBA può essere preferibile quando si vuole una soluzione facilmente estendibile o manutenibile.

Insomma, non esiste necessariamente una soluzione migliore in assoluto: dipende da quale compromesso si vuole privilegiare.

Una cosa che apprezzo di ChatGPT è che riesce a fare da 'traduttore' tra il codice e il ragionamento. Quando mi trovo davanti a formule o routine molto complesse, gli chiedo spesso di scomporle nei passaggi logici. Anche in questo caso non si è fermato alla sintassi, ma ha individuato l'algoritmo comune alle tre soluzioni, evidenziando poi le diverse scelte progettuali.

Mi ha fatto capire che linguaggi diversi possono implementare la stessa idea con filosofie molto diverse. Per chi, come me, sta imparando, questo è stato molto più utile della formula finale.

Alla prossima:StrettaDiMano:
Roberto