Problemă de #Excel – Numerotare automată linii [Updated]

Bună ziua tuturor fanilor Excel. Am pentru voi o provocare, spun eu interesantă, dar probabil și utilă pentru mulți.

Mulțumesc tuturor celor care au participat la rezolvarea problemei sau sunt interesați de ea. Sper să fie utile. Soluțiile primite sunt prezentate în partea de jos a articolului.

În modelul de date din imaginea de mai jos, se cere ca Task-urile (sau alte tipuri de înregistrări de nivel 1, sau Heading 1 pentru cei familiarizați cu aplicațiile Word) trebuie să fie numerotate automat consecutiv. Condiția ca numele din coloana B să înceapă cu Task este obligatorie de respectat în date. Sau oricare alt identificator pentru înregistrările de nivel 1.

În celula A2 este singura care are scris pur și simplul nr. 1. Puteam face și dinamic acel număr cu funcția ROW()-1 în cazul meu. Restul numerelor sunt prin formulă.

Tot ceea ce nu este introdus ca Task (cele de nivel 2 sau Heading 2) trebuie să aibă numere în mod dinamic cu 1.1, 1.2 în funcție de numărul lor. În soluția mea, acel 1.1 nu este chiar un număr.


Pentru cei care vor să participe la această provocare, vă rog să trimiteți soluția prin e-mail la adresa: valy.greavu@outlook.com până la data de 22.05.2020 ora 14:00. Dacă sunt studenți de ai mei de la Master AA care rezolvă problema în termenul specificat le echivalez proba practică de laborator, de săptămâna viitoare cu 10 (primul care trimite cu soluția).

Aștept cu interes! :)

Update ora 22.05.2020 H16:00

 

Soluția mea

Chiar dacă pare o problemă simplă eu m-am complicat destul de mult în formulă.

Formula mea din celula A3 este:

=IF(LEFT(B3;4)=”Task”;MAX($A$2:A2)+1;MAX($A$2:A2)&”.”&(COUNTA(INDIRECT(CONCATENATE(ADDRESS(MATCH(MAX($A$2:A2);$A$2:A2;0)+1;1);”:”;ADDRESS(ROW()-1;1;4))))))

În această formulă compar dacă începutul textului din coloana B este cu Task, dacă da, adun valoarea 1 la maximul tuturor coloanelor de mai sus.

Dacă nu este task, scrie orice altceva fac concatenare cu & cu numărul maxim de mai sus, semnul „.” și rezultatul numărării (COUNTA) a celulelor de pe blocul dinamic de mai sus, format din adresa celulei in care se află valoarea maximă de până acum ADDRESS(MATCH(MAX($A$2:A2);$A$2:A2;0)+1;1) cu adresa celulei de mai sus de poziția curentă (ADDRESS(ROW()-1). Ca să pot face COUNTA() folosesc funcția INDIRECT() pentru a compune dinamic blocul de celule de căutare.

Avantajul este că funcționează și la mai mult de 10 task-uri și mai mult de 10 subtask-uri.

Dezavantajul este la sortare unde apare eroare dacă faci sortare după coloana de numere curente, sau coloana de activități.

Soluția este funcțională și în Foi de calcul Google, cu mențiunea că având un format regional diferit a trebuit să mai adaug un punct la finalul formulei ca să nu apară rezultatul de tip dată calendaristică.

Rezultatul formulei din Google.

 

Soluție Aurora

Mă refer la un prenume nu la un cod de soluție. :)

Aurora o fostă studentă de pe la începutul mileniului acesta, mi-a trimis prima soluție.

Formula ei din A3 este:

=IF(LEFT(B3;3)=”Sub”;IF(ISERROR(FIND(„.”;A2));A2&”.1″;LEFT(A2;FIND(„.”;A2))&(VALUE(RIGHT(A2;LEN(A2)-FIND(„.”;A2)))+1));IF(ISERROR(FIND(„.”;A2));A2+1;VALUE(LEFT(A2;FIND(„.”;A2)-1))+1))

Soluția propune folosirea repetitivă a funcțiilor FIND(), LEFT() și RIGHT() cu adunări de numere consecutive. Testat 100% funcțional la valori de peste 10 task-uri și subtask-uri.

Dezavantajul modelului este că dacă un subtask nu începe cu valoarea Sub, atunci nu mai merge formula.

Soluție Adrian B

Soluția lui Adrian folosește o paletă mai variată de funcții, dar rezultatul este identic cu soluțiile de mai sus.

=IF(B3<>””;IF(LEFT($B3;4)=”Task”;MAX($A$2:$A2)+1;IF(NOT(ISERROR(FIND(„.”;A2)));MID(A2;1;FIND(„.”;A2)-1)&”.”&MID(A2;FIND(„.”;A2)+1;LEN(A2)-FIND(„.”;A2))+1;A2&”.1″));””)

Formula este testată pentru mai mult de 10 task-uri și subtasuri. De asemenea, poate funcționa și pentru subtask-uri care nu au aceasta etichetă.

 

Soluție Alexandru N

Alexandru a trimis o soluție la fel de funcțională ca și celelalte doar că folosește altă abordare și altă combinație de formule. Îmi place la formula lui Alexandru faptul că are o complexitate mai redusă și poate fi mai ușor de înțeles și replicat. Asta dacă reușiți să vă „adaptați” la SUMPRODUCT().

=(IF(LEFT(B3;4)=”Task”;SUMPRODUCT((LEFT($B$2:B3;4)=”Task”)*1);SUMPRODUCT((LEFT($B$2:B3;4)=”Task”)*1)&”.”&IFERROR(RIGHT(A2;LEN(A2)-FIND(„.”;A2;1));0)+1))

Formula testată și funcțională.

 

Soluție Ciprian S

Ciprian, un vechi prieten al acestui gen de probleme și care a venit permanent cu soluții surprinzătoare, a trimis o soluție foarte interesantă ca primă versiune:

=IF(LEFT(B2;4)=”Task”; FLOOR.MATH(SUM(A1))+1; SUM(A1)+0,1)

Formula se scrie în B2 și funcționează pentru versiunile peste 10 task-uri dar cu maxim 9 task-uri.

Formula este testată și funcționează și în Google Sheets.

Pentru versiunea cu peste 10 subtaskuri Ciprian a folosit o variantă asemănătoare cu cele de mai sus:

Formula din A2:

=IF(LEFT(B2;4)=”task”;TEXT(IFERROR(VALUE(LEFT(A1;(FIND(„.”;A1)-1)))+1;IFERROR(VALUE(A1);0)+1);”0″);IFERROR(LEFT(A1;(FIND(„.”;A1)));A1&”.”)&IFERROR(MID(A1;(FIND(„.”;A1)+1);100);0)+1)

Este o abordare mult mai corectă decât cea prezentată în soluția mea, pentru că începe cu calculul fix din prima celulă de date (A2) și nu doar din celula A3.

 

O soluție cu VBA

Pentru cei care mă cunosc știu că nu sunt adeptul VBA. Totuși soluția este foarte interesantă și merită exploatată.

În principiu se creează o funcție în VBA cu Range.IndentLevel. Mai multe detalii puteți consulta aici: https://professor-excel.com/how-to-return-the-indentation-of-a-cell-in-excel/

 

Frumusețea acestei probleme este diversitatea soluțiilor și combinațiilor de formule plus ingeniozitatea oamenilor în a le combina în vederea obținerii rezultatelor asemănătoare. Mulțumesc frumos și ținem legătura! :)

Sper să fie util cuiva.

 

Problemă de #Excel – Cine este plecat în delegație? [UPDATED]

De câteva zile am primit o problemă interesantă de Excel. Și cum mulți dintre cititorii mei sunt avizi după noi provocări vă invit să rezolvați această problemă de Excel, fără utilizare VBA sau coloane ajutătoate.

Cerința problemei:

Într-o instituție oamenii pleacă în delegații. Istoricul de delegații este în tabelul A1:D11 dar poate fi mult mai lung, pentru că acolo sunt păstrate detaliile tuturor deplasărilor. Se dorește să se afișeze un tabel dinamic în care să se afișeze persoanele aflate în delegație în ziua curentă.

Model tabel și raport sintetic. Datele rezultat sunt corespondente zilei de 04.11.2019.

După rezolvare, soluția Excel trebuie transmisă prin e-mail la adresa: valy.greavu@outlook.com până pe 06.11.2019 ora 10:00.

Dacă se poate să treceți în email și o scurtă descriere a rezolvării ar fi super! Dacă nu mă prind eu, cumva. :)

Premiu: 10 „flotări mentale” și recunoștința noastră!

UPDATE 06.11.2019

În urma consultării din public am primit mai multe soluții care de care mai ingenioase.

Soluția mea este o combinație de CSE (acronimul de la funcțiile array Ctrl+Shift+Enter) cu funcțiile INDEX și MATCH cu condiții multiple.

Formula folosită pentru nume este:

=IFNA(INDEX(tblD[Nume_Prenume];MATCH(1&0;IF(TODAY()>=tblD[Data_Plecarii];IF(TODAY()<=tblD[Data_Sosirii];1))&COUNTIF($F$2:F2;tblD[Nume_Prenume]);0));””)

În care tblD este numele tabelului de date iar condițiile cumulative 1&0 sunt corespondente IF-ului din prima parte a MATCH-ului (1) și COUNTIF-ului din partea a doua care determină unicitatea înregistrărilor.

Listingul de la PowerQuery pentru acest model de denumiri este:

let

Source = Excel.CurrentWorkbook(){[Name=”tblD”]}[Content],

#”Changed Type” = Table.TransformColumnTypes(Source,{{„Nr.Crt.”, Int64.Type}, {„Nume_Prenume”, type text}, {„Data_Plecarii”, type datetime}, {„Data_Sosirii”, type datetime}}),

#”Added Custom” = Table.AddColumn(#”Changed Type”, „Plecat”, each Logical.From(DateTime.Date(DateTime.LocalNow()) >= DateTime.Date([Data_Plecarii]) and DateTime.Date(DateTime.LocalNow()) <= DateTime.Date([Data_Sosirii]))),

#”Filtered Rows” = Table.SelectRows(#”Added Custom”, each ([Plecat] = true)),

#”Removed Columns” = Table.RemoveColumns(#”Filtered Rows”,{„Nr.Crt.”, „Data_Plecarii”, „Plecat”}),

#”Changed Type1″ = Table.TransformColumnTypes(#”Removed Columns”,{{„Data_Sosirii”, type date}})

in

#”Changed Type1″

Nu uitați că PQ este case sensitive. Sunt un mare fan al PQ dar acesta nu-și face autorefresh la date în momentul în care se schimbă ceva în sursă. Trebuie refresh manual.

Fișierul cu exemplul rezolvat cu CSE și PQ poate fi consultat sau descărcat de aici: https://1drv.ms/x/s!ApGubfWFh8NurJgyC1qAhvQ8bcjwuQ?e=0eo2dT. Fișierul nu conține macros.

 

Soluția CiprianS

Prima soluție a venit de la un prieten mai vechi Ciprian S.

Ciprian a propus o rezolvare cu PowerQuery care este relativ simplă și funcționează perfect și una cu CSE.

Formula utilizată de Ciprian pentru afișare nume este:

=IFERROR(INDEX(Table1[Nume_Prenume];SMALL(IF(–(TODAY()>=Table1[Data_plecării])–(TODAY()<=Table1[Data_Sosirii])=2;Table1[Nr.Crt.];””);ROWS($K$10:K11)-1));””)

Formula folosește SMALL în loc de MATCH-ul clasic propus de mine. În felul acesta soluția devine puțin mai rapidă pe seturi de date mari.

Soluția SilviuP

O soluție foarte simplă de rezolvare a venit de la SilviuP. Acesta folosește funcția IF imbricată pentru a afișa dacă un anumit om este plecat în delegație sau nu. Pentru cei care nu sunt plecați lasă liber rândul în raport. Din această cauză această soluție nu este una câștigătoare. :)

Proiecție soluție cu IF.

 

Soluția ClaudiaT

O altă soluție simplă a venit de la ClaudiaT. Ea a folosit un pivot table legat la un Timeline Tool pentru a selecta intervalele de date specifice plecării și sosirii. Aparent este o soluție simplă și uneori soluțiile simple sunt mai la îndemâna tuturor!

Proiecție soluția cu Timeline.

 

Soluția CristianA

CristianA a trimis mai multe rezolvări. Așa cum spunea el… prea simplă problema.

În prima soluție El a folosit formula IF asemănător cu soluția lui Silviu apoi a filtrat rezultatele. Eu cred că s-a grăbit puțin.

A doua soluție este asemănătoare ca formă și rezultat cu PowerQuery-ul trimis de Ciprian.

A treia soluție este în schimb destul de abstractă și folosește DAX pentru generarea tabelului de date.

Proiecție rezolvare cu DAX.

De asemenea Cristian mai anunță o metodă de rezolvare de ultimă oră și anume utilizarea Dynamic Arrays. Detalii aici: https://www.excelcampus.com/functions/dynamic-array-formulas-spill-ranges/ și aici: https://www.sumproduct.com/news/article/news/dynamic-arrays-becoming-generally-available

 

Pentru cei care doresc să învețe mai multe despre PowerQuery și formule CSE găsiți detalii în blog și pe canalul meu de Youtube!

 

Sper să fie util cuiva!

#Excel – Aplicare culori de fundal pentru rândurile alternative

De foarte multe ori în Excel trebuie să urmărim linii de date pentru a compara valori sau alte scopuri. Dacă totul este în negru pe alb, operațiunea devine obositoare. Cea mai comună operațiune este să delimitezi cumva liniile între ele prin aplicarea unei formatări de tip Table din meniul Home. Se dă click pe prima celulă din tabelul de date apoi se selectează opțiunea Format as Table. Sigur acest lucru ne va ajuta ulterior și la formule prin ușurarea modului de scriere a referințelor din acestea.

Ce facem în schimb când tabelul nostru nu permite formatarea ca un Table? Cum adică să nu meargă?

Iată un mesaj de eroare mai jos.

Sau atunci când datele sunt ținute în formatul de raport direct și sunt „îmbunătățite” cu multe operațiuni Merge sau alte formatări „faine”.

 

În cazul în care nu doriți să utilizați formatarea ca tabel, există opțiunea formatării manuale… Glumeam, desigur! :)

 

Pentru formatarea alternativă cu o culoare de fundal, putem încerca utilizarea formatării condiționate bazate pe linia pe care se află datele.

Ca să aflăm valoarea liniei pe care se află datele putem utiliza funcția =ROW(). Funcția respectivă returnează un număr par sau impar care împărțit la 2 va returna un număr cu zecimale xyz,5 pentru numerele impare și un număr fără zecimale pentru numerele pare. Folosind funcția de determinare a întregului (INT()) din împărțirea numărului liniei la 2 și scăzând această valoare din valoarea inițială vom obține alternativ pentru liniile din Excel valorile 0 (zero) sau 0,5. Pe baza acestei valori putem aplica formatarea condiționată și aceasta se păstrează pentru tot tabelul de date indiferent dacă facem sortare sau filtrare.

În final funcția de determinare a rândului par sau impar ar trebui să fie:

=ROW()/2-INT(ROW()/2)

Pas cu pas.

  1. Se selectează prima celulă din tabelul cu date;
  2. Se apasă combinația de taste Ctrl+A pentru selectarea întregului tabel
  3. Din meniul Home se alege Conditional Formatting, New Rule

  4. În fereastra New Formatting Rule se alege opțiunea Use a formula to determine which cells to format apoi în secțiunea de formulă se introduce formula:

    =ROW()/2-INT(ROW()/2)=0

  5. Se apasă apoi butonul Format și din secțiunea Fill se alege culoarea de fundal dorită după care se confirmă cu Ok.

     

    Observație: În cazul în care capul de tabel începe de la o linie pară și nu doriți ca acesta să fie formatat la fel cu restul liniilor atunci puteți folosi funcția: =ROW()/2-INT(ROW()/2)>0

Microsoft are pe site-ul său o metodă mai elegantă dar puțin mai abstractă a acestei formule, recomandând utilizarea funcției MOD. Conform articolului, funcția de la punctul 4 din etapele de mai sus devine:

=MOD(ROW();2)=0

Cam atât. Sper să vă fie util!

 

Blog la WordPress.com.

SUS ↑