[C#] Hitta dubbletter i en ListView?

Permalänk
Medlem

[C#] Hitta dubbletter i en ListView?

Sitter här och försöker återuppta mina programmeringskunskaper som legat på hyllan dom sista 15 åren ungefär, så jag är aningens ringrostig kan man väl säga. Sist var det Borland Delphi jag satt och lekte lite med, men nu är det Microsoft Visual Studio Express 2013 och C# som gäller.

Skulle behöva gå igenom en ListView efter dubbletter och har knåpat ihop följande kod, som förvisso fungerar men jag är övertygad om att det finns "snyggare" sätt att lösa det på. Nån som har några förslag?

bool duplicates = false; for (int a = 0; a < listView1.Items.Count; a++) { string ext = listView1.Items[a].SubItems[2].Text; for (int b = a+1; b < listView1.Items.Count; b++) { string ext2 = listView1.Items[b].SubItems[2].Text; if (ext == ext2) duplicates = true; } }

Visa signatur

AMD Ryzen 7 7800X3D • ASUS TUF Gaming B650-Plus WiFi • Noctua NH-D15
XFX Radeon RX 6950 XT Speedster MERC 319 • MSI Optix MAG271CQR • Dell UltraSharp U2515H
G.Skill 32GB DDR5 6000MHz CL30 • WD Black SN750 NVMe SSD 1 TB • Crucial P3 Plus NVMe SSD 1 TB
Phanteks P600S • ASUS TUF Gaming 850W Gold • Logitech Craft Keyboard • Logitech MX Master 3

Permalänk
Medlem

loopa igenom och kolla om alla läggs till i ett set, om någon inte läggs till beror det på att den redan finns (dvs dubblett)

är inte helt hundra på om jag skrivit bra C# kod, är mer Java-utvecklare. (går att göra hasDuplicates mer generisk, ta en lista av typen T och kolla om alla läggs till)

using System; using System.Collections.Generic; public class Test { public static void Main() { string[] text = new string[7] {"hej","jag","heter","viktor","hej","hallo","hej"}; Console.WriteLine(hasDuplicates(text));; } public static bool hasDuplicates (string[] strings) { HashSet<String> noDups = new HashSet<string>(); foreach (string s in strings) { if(!noDups.Add(s)) { return true; } } return false; } }

EDIT:
går även med

myStrings.Distinct().Count() != myStrings.Count());

eller bara distinct om du endast vill ha en lista med unika exemplar

Permalänk
Medlem
Skrivet av immutable:

Sitter här och försöker återuppta mina programmeringskunskaper som legat på hyllan dom sista 15 åren ungefär, så jag är aningens ringrostig kan man väl säga. Sist var det Borland Delphi jag satt och lekte lite med, men nu är det Microsoft Visual Studio Express 2013 och C# som gäller.

Skulle behöva gå igenom en ListView efter dubbletter och har knåpat ihop följande kod, som förvisso fungerar men jag är övertygad om att det finns "snyggare" sätt att lösa det på. Nån som har några förslag?

bool duplicates = false; for (int a = 0; a < listView1.Items.Count; a++) { string ext = listView1.Items[a].SubItems[2].Text; for (int b = a+1; b < listView1.Items.Count; b++) { string ext2 = listView1.Items[b].SubItems[2].Text; if (ext == ext2) duplicates = true; } }

Kolla så att varje listitems subitem[2]-text förekommer exakt en gång i hela listan:

listView.Items.All(item => listView.Items.Count(x => x.SubItems[2].Text == item.SubItems[2].Text) == 1)

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem
Skrivet av gothxx:

loopa igenom och kolla om alla läggs till i ett set, om någon inte läggs till beror det på att den redan finns (dvs dubblett)

är inte helt hundra på om jag skrivit bra C# kod, är mer Java-utvecklare. (går att göra hasDuplicates mer generisk, ta en lista av typen T och kolla om alla läggs till)

using System; using System.Collections.Generic; public class Test { public static void Main() { string[] text = new string[7] {"hej","jag","heter","viktor","hej","hallo","hej"}; Console.WriteLine(hasDuplicates(text));; } public static bool hasDuplicates (string[] strings) { HashSet<String> noDups = new HashSet<string>(); foreach (string s in strings) { if(!noDups.Add(s)) { return true; } } return false; } }

EDIT:
går även med

myStrings.Distinct().Count() != myStrings.Count());

eller bara distinct om du endast vill ha en lista med unika exemplar

Dold text

Tack för svar!

Är det bättre att göra på det sättet du säger? Alltså rent prestandamässigt? Blir ju ändå dubbla for looper i och med att jag först måste skapa en array.

Skrivet av Teknocide:

Kolla så att varje listitems subitem[2]-text förekommer exakt en gång i hela listan:

listView.Items.All(item => listView.Items.Count(x => x.SubItems[2].Text == item.SubItems[2].Text) == 1)

Dold text

Tack för svar!

Du får gärna utveckla lite hur du menar med ett fungerande exempel. Ser simpelt ut men får det inte att fungera.

Visa signatur

AMD Ryzen 7 7800X3D • ASUS TUF Gaming B650-Plus WiFi • Noctua NH-D15
XFX Radeon RX 6950 XT Speedster MERC 319 • MSI Optix MAG271CQR • Dell UltraSharp U2515H
G.Skill 32GB DDR5 6000MHz CL30 • WD Black SN750 NVMe SSD 1 TB • Crucial P3 Plus NVMe SSD 1 TB
Phanteks P600S • ASUS TUF Gaming 850W Gold • Logitech Craft Keyboard • Logitech MX Master 3

Permalänk
Inaktiv
Skrivet av immutable:

Är det bättre att göra på det sättet du säger? Alltså rent prestandamässigt? Blir ju ändå dubbla for looper i och med att jag först måste skapa en array.

Nej. Båda lösningarna tar nlogn tid.

Permalänk
Medlem
Skrivet av immutable:

Tack för svar!

Är det bättre att göra på det sättet du säger? Alltså rent prestandamässigt? Blir ju ändå dubbla for looper i och med att jag först måste skapa en array.

Tack för svar!

Du får gärna utveckla lite hur du menar med ett fungerande exempel. Ser simpelt ut men får det inte att fungera.

Det där ska vara ett fungerande exempel.

var duplicates = och sedan det jag skrev (förutsatt att din lista heter listView naturligtvis)

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem
Skrivet av Teknocide:

Det där ska vara ett fungerande exempel.

var duplicates = och sedan det jag skrev (förutsatt att din lista heter listView naturligtvis)

Det jag försöker göra är alltså att välja ett antal filer, och om flera filer har samma extension så vill jag bli påmind om detta. Känns som det mest korrekta vore att kontrollera detta innan man lägger till dom i listviewen men kom inte på nåt bra sätt att göra det på.

Lägger upp en bild på felmeddelandet så kanske du kan hjälpa mig. Som sagt jag är helt ny på C# och vet inte alls hur jag ska lösa det.

Visa signatur

AMD Ryzen 7 7800X3D • ASUS TUF Gaming B650-Plus WiFi • Noctua NH-D15
XFX Radeon RX 6950 XT Speedster MERC 319 • MSI Optix MAG271CQR • Dell UltraSharp U2515H
G.Skill 32GB DDR5 6000MHz CL30 • WD Black SN750 NVMe SSD 1 TB • Crucial P3 Plus NVMe SSD 1 TB
Phanteks P600S • ASUS TUF Gaming 850W Gold • Logitech Craft Keyboard • Logitech MX Master 3

Permalänk
Medlem
Skrivet av immutable:

Det jag försöker göra är alltså att välja ett antal filer, och om flera filer har samma extension så vill jag bli påmind om detta. Känns som det mest korrekta vore att kontrollera detta innan man lägger till dom i listviewen men kom inte på nåt bra sätt att göra det på.

Lägger upp en bild på felmeddelandet så kanske du kan hjälpa mig. Som sagt jag är helt ny på C# och vet inte alls hur jag ska lösa det.

Du behöver importera System.Linq med using System.Linq;

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Medlem

Verkligen inte snabbast, men trevlig om du snabbt vill ha tillgång till de items som är dubbletter.

var dict = listView1.Items.GroupBy(item => item.SubItems[1].ToUpper()).ToDictionary(g => g.Key, g => g.ToList())

Bör du få en dictionary med lista av items per filsuffix. Enkel sak därefter att traversera uppslaget och agera på de filsuffixer med fler än en item.

Nåt sånt...

Skickades från m.sweclockers.com

Visa signatur

-- FubbHead

Permalänk
Medlem

Tack för hjälpen, ska testa lite när jag kommer hem från jobbet.

Skickades från m.sweclockers.com

Visa signatur

AMD Ryzen 7 7800X3D • ASUS TUF Gaming B650-Plus WiFi • Noctua NH-D15
XFX Radeon RX 6950 XT Speedster MERC 319 • MSI Optix MAG271CQR • Dell UltraSharp U2515H
G.Skill 32GB DDR5 6000MHz CL30 • WD Black SN750 NVMe SSD 1 TB • Crucial P3 Plus NVMe SSD 1 TB
Phanteks P600S • ASUS TUF Gaming 850W Gold • Logitech Craft Keyboard • Logitech MX Master 3

Permalänk
Datavetare

Av alla lösningar som föreslagits här är den med HashSet<> överlägset bäst sett till tidskomplexitet, den är linjär med avseende på antal element i din lista, d.v.s. O(N). Enda mindre nackdelen är att den är den också tar utrymme linjärt mot N, (auxiliary space är O(N)). Denna lösning ger svaret snabbt och du kan även reda ut vilka element som är duplikat.

Näst bäst, om du bara vill veta om det finns duplikat eller ej, är varianten som använder Distinct(). Den kommer ta N*log(N) tid och, förutsatt att man sorterar på ett vettigt sätt, ta utrymme motsvarande log(N) (så är bättre än HashSet<> om utrymme är ett problem).

Sedan kommer dubbel loop, den har fördelen att ta konstant utrymme men har kvadratisk tidskomplexitet, O(N^2)

Värst på alla sätt och vis är Linq lösning (testade bara den som avgör om det finns duplikat eller ej, den som även kan visa duplikaten har samma tidskomplexitet men äter ännu lite mer RAM). Den är överlägset långsammast, den genererar väldigt mycket kortlivade objekt som (temporärt) äter RAM och slöar ner systemet då skräpsamlaren reder ut vilket minne som inte längre används. Rent tidskomplexitetsmässigt är denna O(N^2) och är rätt omöjligt att veta hur mycket utrymme den tar utan att gå igenom implementationen av de saker Linq använder.

Gjorde ett snabbt test på en array med 100.000 element. Varianten med HashMap fixar det i princip omedelbart, Distinct ligger väldigt nära men är något långsammare. Dubbla for-loopar är ca 1000 gånger långsammare än HashMap medan Linq är ungefär en tiopotens till långsammare.

Med Linq kan man göra en del eleganta lösningar, men man ska nog undvika denna finess om effektivitet är ett krav och alltid undvika det i fall där indata är väldigt stort då det blir en hel del overhead.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem

Gör det som är lättast att läsa och tyda din avsikt med. Jag gissar att det rör sig om så få objekt att eventuella optimeringsvinster är försumliga på vilken dator som helst denna sida 2000-talet.

Visa signatur

-- FubbHead

Permalänk
Medlem
Skrivet av Yoshman:

Av alla lösningar som föreslagits här är den med HashSet<> överlägset bäst sett till tidskomplexitet, den är linjär med avseende på antal element i din lista, d.v.s. O(N). Enda mindre nackdelen är att den är den också tar utrymme linjärt mot N, (auxiliary space är O(N)). Denna lösning ger svaret snabbt och du kan även reda ut vilka element som är duplikat.

Näst bäst, om du bara vill veta om det finns duplikat eller ej, är varianten som använder Distinct(). Den kommer ta N*log(N) tid och, förutsatt att man sorterar på ett vettigt sätt, ta utrymme motsvarande log(N) (så är bättre än HashSet<> om utrymme är ett problem).

Sedan kommer dubbel loop, den har fördelen att ta konstant utrymme men har kvadratisk tidskomplexitet, O(N^2)

Värst på alla sätt och vis är Linq lösning (testade bara den som avgör om det finns duplikat eller ej, den som även kan visa duplikaten har samma tidskomplexitet men äter ännu lite mer RAM). Den är överlägset långsammast, den genererar väldigt mycket kortlivade objekt som (temporärt) äter RAM och slöar ner systemet då skräpsamlaren reder ut vilket minne som inte längre används. Rent tidskomplexitetsmässigt är denna O(N^2) och är rätt omöjligt att veta hur mycket utrymme den tar utan att gå igenom implementationen av de saker Linq använder.

Gjorde ett snabbt test på en array med 100.000 element. Varianten med HashMap fixar det i princip omedelbart, Distinct ligger väldigt nära men är något långsammare. Dubbla for-loopar är ca 1000 gånger långsammare än HashMap medan Linq är ungefär en tiopotens till långsammare.

Med Linq kan man göra en del eleganta lösningar, men man ska nog undvika denna finess om effektivitet är ett krav och alltid undvika det i fall där indata är väldigt stort då det blir en hel del overhead.

I ett GUI-fall är tidskomplexitet kanske inte så viktigt. Vad är det för temporära objekt du menar att den booleska Linq-lösningen skapar? Den borde inte skapa något förutom två anonyma funktioner.

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Datavetare
Skrivet av Teknocide:

I ett GUI-fall är tidskomplexitet kanske inte så viktigt. Vad är det för temporära objekt du menar att den booleska Linq-lösningen skapar? Den borde inte skapa något förutom två anonyma funktioner.

Rätt 100 på att

item => arr.Count(x => x == item)

skapar en iterator internt och det löser C#/.Net med att skapa ett objekt som implementerar en tillståndsmaskin. Detta görs för varje element i listan. Men svårt att se hur det skulle kunna förklara en tio till ett skillnad i prestanda mot varianten med två for-loopar, så måste finnas mer overhead än så.

Tidskomplexiteten i GUI är inte viktigt, men däremot så är total exikveringstid för alla saker som körs i GUI-tråden kritiskt om man vill ha ett GUI som inte "laggar". Sedan skapade TS denna tråd för att fråga om det fanns en mer effektiv lösning än den som visas i första posten, Linq-lösningarna är de enda som är mindre effektiva.

Skulle också säga att lösningen med HashSet inte bara är den mest effektiva, det är den som är absolut lättast att förstå av de versioner som även kan peka ut exakt vilka element som är duplikat. Enklast är väl varianten som använder Distinct().

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem
Skrivet av Yoshman:

Rätt 100 på att

item => arr.Count(x => x == item)

skapar en iterator internt och det löser C#/.Net med att skapa ett objekt som implementerar en tillståndsmaskin. Detta görs för varje element i listan. Men svårt att se hur det skulle kunna förklara en tio till ett skillnad i prestanda mot varianten med två for-loopar, så måste finnas mer overhead än så.

Tidskomplexiteten i GUI är inte viktigt, men däremot så är total exikveringstid för alla saker som körs i GUI-tråden kritiskt om man vill ha ett GUI som inte "laggar". Sedan skapade TS denna tråd för att fråga om det fanns en mer effektiv lösning än den som visas i första posten, Linq-lösningarna är de enda som är mindre effektiva.

Skulle också säga att lösningen med HashSet inte bara är den mest effektiva, det är den som är absolut lättast att förstå av de versioner som även kan peka ut exakt vilka element som är duplikat. Enklast är väl varianten som använder Distinct().

Count borde väl kunna lösa det internt med en vanlig loop men du har nog rätt.

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Datavetare
Skrivet av Teknocide:

Count borde väl kunna lösa det internt med en vanlig loop men du har nog rätt.

Count är en Linq-metod implementerad via "extension methods" (hela Linq är implementerat så). Det sätt som C# implementerar "extension methods" gör det inte möjligt att optimera implementation beroende på underliggande dynamisk typ, utan man måste göra en implementation som inte antar något annat än IEnumerable vilket betyder att man får iterera igenom allt. C++ skulle kunna göra optimeringar via template specialisering, Java8 kan optimera (vilket även sker i praktiken) motsvarande sak i "streams" då "default/defender methods" har dynamisk binding och inte statisk binding som C#.

Man kan i.o.f.s. använda AsParallel()

var duplicate = arr.AsParallel().All(item => arr.Count(x => x == item) == 1);

skalade väldigt bra i det exempel jag gjorde men trots det så blev det långsammare än två for-loopar + PLinq använder alla 8 CPU-trådar till 100% mot bara 1 för andra lösningar, så inte speciellt effektivt och inte så snällt mot andra saker som eventuellt kör i systemet.

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Medlem
Skrivet av Yoshman:

Count är en Linq-metod implementerad via "extension methods" (hela Linq är implementerat så). Det sätt som C# implementerar "extension methods" gör det inte möjligt att optimera implementation beroende på underliggande dynamisk typ, utan man måste göra en implementation som inte antar något annat än IEnumerable vilket betyder att man får iterera igenom allt. C++ skulle kunna göra optimeringar via template specialisering, Java8 kan optimera (vilket även sker i praktiken) motsvarande sak i "streams" då "default/defender methods" har dynamisk binding och inte statisk binding som C#.

Man kan i.o.f.s. använda AsParallel()

var duplicate = arr.AsParallel().All(item => arr.Count(x => x == item) == 1);

skalade väldigt bra i det exempel jag gjorde men trots det så blev det långsammare än två for-loopar + PLinq använder alla 8 CPU-trådar till 100% mot bara 1 för andra lösningar, så inte speciellt effektivt och inte så snällt mot andra saker som eventuellt kör i systemet.

Om jag inte minns fel så görs vissa sådana optimeringar genom att de kollar på runtime-typen av T.

edit: Dock inte för Count(Func<>...)
http://www.dotnetframework.org/default.aspx/4@0/4@0/untmp/DEV...

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Avstängd

precis Count() gör den kollen, är det en IList kommer den gå direkt mot Count propen
Men Count(predicate) kan inte göra det

Visa signatur
Permalänk
Avstängd

Btw, välldigt, välldigt sällan det är kod som denna som skapar prestandaproblem. Oftast är det databas connections och liknande problem. Tex att en utvecklare glömt .Include på en EF query vilket resulterar i att man lazyloadar 200 000 rader från DB

edit: Jag har hört om ett system där utvecklarna inte använbt Include på massor av ställen, men deras testdatamängd var för liten så de märkte inte flaskhalsen så det var först när systmet gick prod som det märktes.. haha..

Visa signatur
Permalänk
Medlem
Skrivet av CyberVillain:

precis Count() gör den kollen, är det en IList kommer den gå direkt mot Count propen
Men Count(predicate) kan inte göra det

Vilket Egentligen är ett anti-pattern, eftersom uppsättningen av optimeringar effektivt begränsas till de typer man tog med i beräkningen när man implementerade Enumerable. Detta är en av nackdelarna med reification av typparametrar, vilket i sig är ett väldigt intressant ämne men även väldigt orelaterat till frågan som ställdes så jag får lugna ner mig nu

Visa signatur

Kom-pa-TI-bilitet

Permalänk
Avstängd

Jo det är verkligen ett antipattern polymorfistiskt (är det ett ord?)
Dock i ett ramverk som Linq kanske man tyvärr måste ta sådana genvägar (omvägar). Dock händer det att jag stöter på samma sak i domänlogik och då tar jag fram min dumstrut jag har i skrivbordslådan och sätter den på utvecklaren i fråga

Visa signatur
Permalänk
Datavetare
Skrivet av CyberVillain:

Btw, välldigt, välldigt sällan det är kod som denna som skapar prestandaproblem. Oftast är det databas connections och liknande problem. Tex att en utvecklare glömt .Include på en EF query vilket resulterar i att man lazyloadar 200 000 rader från DB

edit: Jag har hört om ett system där utvecklarna inte använbt Include på massor av ställen, men deras testdatamängd var för liten så de märkte inte flaskhalsen så det var först när systmet gick prod som det märktes.. haha..

Det är exakt den slutsats jag dragit när jag suttit och testat ganska mycket olika saker i C#/.Net. Om folk verkligen skrev saker i .Net där det man själv utvecklade var prestandakritiskt skulle man inte längre använda .Net för det presterar rätt dåligt. Frågan är varför MS inte jobbar mer på att fixa till det, för något hände mellan Java6 och Java7 som resulterade i rejält boost i många CPU-bunda program. Oracle har också sagt att runtime-plattformen effektivitet är något de ser som det mest kritiska för dem vad det gäller Java.

Java har idag en väsentligt mycket effektivare runtime-plattform än .Net. Skriver man om just det här Linq-exemplet med Java8 streams så blir det ungefär en tiopotens snabbare på min maskin. Även dubbla for-loop versionen blir 3-4 gånger snabbare.

Men precis som du säger är det säkert ett icke-problem i de program folk typiskt skriver i .Net för det man skriver är bara ett tunt lager mellan en databas och någon form av nätverksanslutet server-ramverk (typiskt IIS).

Visa signatur

Care About Your Craft: Why spend your life developing software unless you care about doing it well? - The Pragmatic Programmer

Permalänk
Avstängd

I et CRUD-system är det ju som du säger. Men de flesta system man skriver i .NET är mycket mer komplexa än så, du har domänspecifika affärsregler, beräkningar, integration, event stores. etc

edit: Men självklart får MS gärna jobba på prestandan. Java är ju ett pruttspråk jämfört med C# på andra saker. Som generics och något som jag irriterar mig på är att den kräver att man fångar undantag hela tiden. Undantag ska man bara fånga om man kan hantera dem. Resten sköter appdomänen i form av loggning etc

Visa signatur