[ Shavgan @ 15.11.2011. 10:44 ] @
Problem je sljedeći. Potrebno je napraviti formu u Visual Studiu 2010 u kojoj će se ispisivati (listview ili combobox itd) duplikati tipa string liste i zbroj duplikata. (Npr. ako se unutar liste tipa string "dobardan" ponavlja 4 puta da ispise na sljedeci nacin: (dobardan: 4) Ja sam uradio tako da mi samo ispise duplikate ali ne znam kako da mi prikaze i broj ponavljanja u listi za dati string!!! hvala puno unaprijed



List<string> list = new List<string>();
list.Add("adnan");
list.Add("dado");
list.Add("mirza");
list.Add("mirza");
list.Add("adnan");
list.Add("adis");


List<string> novalist = list.FindAll(delegate(string i)
{

return list.FindAll(delegate(string j)
{

return j == i;

}).Count() > 1;

}).Distinct().ToList();
foreach (string value in novalist)
{
Console.WriteLine("Duplikati: {0}", value);

}


Output: adnan
mirza
[ Dejan Carić @ 15.11.2011. 11:16 ] @
Code:
var q = from x in list
        group x by x into g
        let count = g.Count()
        where count > 1
        orderby count descending
        select new {Value = g.Key, Count = count};

foreach (var x in q)
{
    Console.WriteLine("Value: " + x.Value + " Count: " + x.Count);
}
[ Shavgan @ 15.11.2011. 13:47 ] @
Nesto ovako :

new List<int>{1,1,1,2,2,3,4,5,6}
.ToLookup(k => k)
.Where(i => !i.Count().Equals(1))
.ToList().ForEach(i =>
Console.WriteLine("{0} duplikata se ponavlja {1} puta.", i.Key, i.Count())
);

Ali meni konkretno treba sljedeće. Ja sam listu dobio dinamički, tj pozivajući funkciju da mi čita imena dijelova koji čine jednu konstrukciju iz jednog CAD alata (SolidEdge-a) Ta imena sam prikazao pomoću listView controle. Unutar listview imam i imena dupliciranih dijelova,tj. prikazana su npr.dva,tri puta. Sad je potrebno iz te liste izvucem imena duplikata i pored imena da pise koliko puta se taj dio duplicirao. Prikazati se moze u jos jednom listview ili combobox-u koji je readonly. Mislim da sam uspio predociti moj problem...nadam se da ce bizi pomoci od strane forumasa...
[ mmix @ 15.11.2011. 13:56 ] @
nije ti bas efikasno to resenje, ono koje ti je Dejan dao je mnogo bolje.
[ sallle @ 15.11.2011. 14:39 ] @
Ovaj tip problema se pojavljuje u ovim zajebanim softverskim firmama na testiranjima (fb,google, ms...), i prebrojavanje treba raditi koristeci hash tabelu.
U principu i ovaj linq iz prethodnih postova ispod haube verovatno radi slicno...

Code:

Dictonary<string,int> dict = new Dictionary<string,int>()
foreach (var rec in list)
{
if (dict.containsKey(rec))
   dict[rec]++;
else
   dict.insert(rec,1);
}
[ Dejan Carić @ 15.11.2011. 15:26 ] @
Dictionary jeste brži ali samo pod uslovom da rec nikada nije null.
S obzirom da je string referentni tip, tako nešto je ipak moguće i tu dict[rec]++; puca.

GroupBy je sporiji jer ima tu proveru ali zato sa njim možeš dohvatiti broj stringova koji su null.
[ Boris B. @ 26.11.2011. 00:10 ] @
Citat:
Dejan CarićDictionary jeste brži ali samo pod uslovom da rec nikada nije null.
S obzirom da je string referentni tip, tako nešto je ipak moguće i tu dict[rec]++; puca.

GroupBy je sporiji jer ima tu proveru ali zato sa njim možeš dohvatiti broj stringova koji su null.


Zanimljivo, ispada da je Dictionary implementacija oko 2x sporija od one tvoje implementacije sa čistim linq query-em:
Code (csharp):

        private void button1_Click(object sender, EventArgs e)
        {
            var wordlist = new[] { "lorem", "ipsum", "dolor", "sit", "amet", "null" };
            var rnd = new Random(42);
            var sample = new List<string>(10000);
            for (var i = 0; i < 10000; i++)
                sample.Add(wordlist[rnd.Next(wordlist.Count())]);

            var start = DateTime.Now.Ticks;
            for(var i = 0; i < 1000; i++)
            {
                var res = (from x in sample
                         group x by x into g
                         let count = g.Count()
                         where count > 1
                         orderby count descending
                         select new {Value = g.Key, Count = count}).ToList();
            }
            System.Diagnostics.Debug.Print(new TimeSpan(DateTime.Now.Ticks - start).ToString());
        }

        private void button2_Click(object sender, EventArgs e)
        {
            var wordlist = new[] { "lorem", "ipsum", "dolor", "sit", "amet", "null" };
            var rnd = new Random(42);
            var sample = new List<string>(10000);
            for (var i = 0; i < 10000; i++)
                sample.Add(wordlist[rnd.Next(wordlist.Count())]);

            var start = DateTime.Now.Ticks;
            for (var i = 0; i < 1000; i++)
            {
                var res = new Dictionary<string, int>();
                foreach (var s in sample)
                    if (res.ContainsKey(s))
                        res[s]++;
                    else
                        res.Add(s, 1);
            }
            System.Diagnostics.Debug.Print(new TimeSpan(DateTime.Now.Ticks - start).ToString());
        }
 


Rezultat (i7-930):
00:00:00.8290474 -- Linq group
00:00:01.3500773 -- Dictionary

Nema null vrednosti, da bi bilo "fer". Rezultat Dictionary implementacije još uključuje i rezultate sa jednim ponavljanjem, znači trebao bi i još jedan filter na kraju.


EDIT:
Ruku na srce Dictionary implementacija može da se malo bolje napiše, da se izbegne trodupli lookup, jer indekser++ u stvari radi set(get()+1) plus jedan za ContainsKey():
Code (csharp):

    int count;
    foreach (var s in sample)
        if (res.TryGetValue(s, out count))
            res[s] = count + 1;
        else
            res.Add(s, 1);
 

Ovo se izvršava za 00:00:00.9590548, što je dosta bolje mada još uvek sporije od linq implementacije.

[Ovu poruku je menjao Boris B. dana 26.11.2011. u 02:11 GMT+1]
[ sallle @ 26.11.2011. 03:02 ] @
Borise, sta kaze output za ovu varijantu?

Code:

var res = new Dictionary<string, Broj>();
                Broj b;
                foreach (var s in sample)
                    if (res.TryGetValue(s, out b))
                        b.val++;
                    else
                        res.Add(s, new Broj() { val = 1 });


Code:

public class Broj
    {
        public int val;
    }


[ Boris B. @ 26.11.2011. 11:06 ] @
Da, to je "dobitna kompbinacija", izbegnes double lookup sa referencom.

Sad se izvršava za 00:00:00.6042314. Kako bi još efikasno uključio i brojanje null-ova?


[ sallle @ 26.11.2011. 11:49 ] @

Code:

var res = new Dictionary<string, Broj>();
int nullCount=0;
                Broj b;
                foreach (var s in sample)
                {
                    if (s == null)
                    {
                        nullCount++;
                        continue;
                    }
                    if (res.TryGetValue(s, out b))
                        b.val++;
                    else
                        res.Add(s, new Broj() { val = 1 });
                  }


btw, ako u wordlist ubacis null, ovo ce jos brze da radi nego prethodna verzija bez provere null-a. (jer za nullove nece raditi lookup)