2010-01-24 77 views
9

Je nettoie 5 fichiers pour une valeur spécifique. Je ne prévois aucune valeur différente, MAIS puisque c'est pour mes propres buts éducatifs, je voudrais que l'application compte, compare et imprime la valeur la plus populaire.Comparer/compter les valeurs dans un System.Collections.ArrayList

par exemple:

ArrayList arrName = new ArrayList(); 
arrName.Add("BOB") 
arrName.Add("JOHN") 
arrName.Add("TOM") 
arrName.Add("TOM") 
arrName.Add("TOM") 

Le résultat que je voudrais va être TOM, mais être un novice, je ne sais pas vraiment comment aller de l'avant.

Toutes pensées, suggestions ou exemples sont grandement appréciés. Merci.

Répondre

1

Vous pouvez utiliser un dictionnaire (.NET 2.0+) Pour maintenir le nombre répété de chaque valeur:

Dictionary<string, int> counts = new Dictionary<string, int>(); 
foreach (string name in arrName) { 
    int count; 
    if (counts.TryGetValue(name, out count)) { 
     counts[name] = count + 1; 
    } else { 
     counts.Add(name, 1); 
    } 
} 

// and then look for the most popular value: 

string mostPopular; 
int max = 0; 
foreach (string name in counts.Keys) { 
    int count = counts[name]; 
    if (count > max) { 
     mostPopular = name; 
     max = count; 
    } 
} 

// print it 
Console.Write("Most popular value: {0}", mostPopular); 

Si vous utilisez C# 3.0 (.NET 3.5 +) puis utilisez:

var mostPopular = (from name in arrName.Cast<string>() 
        group name by name into g 
        orderby g.Count() descending 
        select g.Key).FirstOrDefault(); 

Console.Write("Most popular value: {0}", mostPopular ?? "None"); 
0

Pour vous déplacer dans la boucle, vous pouvez utiliser un foreach:

foreach (string name in arrName) { 
    Console.WriteLine(i); 
} 

Et pour compter les valeurs, vous pouvez utiliser un Hashtable, qui liste les variables. La clé peut être un nom, et la valeur peut être combien de fois vous avez vu ce nom dans la liste.

Hashtable nameHash = new Hashtable(); 
foreach (string name in arrName) { 
    if (!nameHash.ContainsKey(name)) { 
     nameHash.Add(name, 1); 
    } 
    else { 
     int num = nameHash[name]; 
     nameHash.Add(name, num + 1); 
    } 
} 
1
public static string GetMostPopular(ArrayList vals) 
    { 
     IDictionary<string, int> dict = new Dictionary<string, int>(); 
     int mx = 0; 
     string ret = ""; 
     foreach (string x in vals) 
     { 
      if (!dict.ContainsKey(x)) 
      { 
       dict[x] = 1; 
      } 
      else 
      { 
       dict[x]++; 
      } 
      if (dict[x] > mx) 
      { 
       mx = dict[x]; 
       ret = x; 
      } 
     } 
     return ret; 
    } 

    static void Main() 
    { 
     ArrayList arrName = new ArrayList(); 
     arrName.Add("BOB"); 
     arrName.Add("JOHN"); 
     arrName.Add("TOM"); 
     arrName.Add("TOM"); 
     arrName.Add("TOM"); 
     string ans = GetMostPopular(arrName); 
     Console.WriteLine(ans); 
    } 
2

Vous n'avez pas spécifié la version de .Net/C# que vous utilisez, donc je vais traiter cette opération pour chaque version de C#: v1, v2 et v3.

C# V1:

class CountValueComparer : IComparer 
{ 
    public int Compare(object x, object y) 
    { 
     DictionaryEntry left = (DictionaryEntry)x; 
     DictionaryEntry right = (DictionaryEntry)y; 

     return ((int)left.Value).CompareTo((int)right.Value); 
    } 
} 

Hashtable counts = new Hashtable(); 

foreach(String value in arrName) 
{ 
    if (counts.ContainsKey(value)) 
    { 
     int valueCount = (int)counts[value]; 
     ++valueCount; 
     counts[value] = valueCount; 
    } 
    else 
    { 
     counts[value] = 1; 
    } 
} 

DictionaryEntry[] sorted = new DictionaryEntry[counts.Count]; 
counts.CopyTo(sorted, 0); 
Array.Sort(sorted, new CountValueComparer()); 

foreach (DictionaryEntry entry in sorted) 
{ 
    Console.Writeline("Name: {0}; Count: {1}", entry.Key, entry.Value); 
} 

C# V2:

class CountValueComparer : IComparer<KeyValuePair<String, int>> 
{ 
    public int Compare(int x, int y) 
    { 
     return x.Value.CompareTo(y.Value); 
    } 
} 

// if v2, use the List<T> class! 
List<String> arrName = new List<String>(); 

arrName.Add("TOM"); 
// etc... 

Dictionary<String, int> counts = new Dictionary<String, int>(); 

foreach(String value in arrName) 
{ 
    int count; 
    if (counts.TryGetValue(value, out count)) 
    { 
     counts[value] = ++count; 
    } 
    else 
    { 
     counts[value] = 1; 
    } 
} 

KeyValuePair<String, int>[] sorted = new KeyValuePair<String, int>[counts.Count]; 
counts.CopyTo(sorted, 0); 
Array.Sort(sorted, new CountValueComparer()); 

C# V3:

// if v3, use the List<T> class! 
var arrName = new List<String>(); 

arrName.Add("TOM"); 
// etc... 

var counts = (from n in arrName 
       group n by n into g 
       select new { Name = g.Key, Count = g.Count() }) 
       .OrderByDescending(x => x.Count); 
var top = counts.FirstOrDefault(); 
Console.WriteLine("Name: {0}; Count: {1}", top.Name, top.Count); 
2

C'est le genre de tâche pour laquelle LINQ est bien adapté.

Tout d'abord, nous allons définir ce que nous faisons:

  1. groupe les éléments de valeur
  2. Count chaque groupe
  3. Retour l'élément dont le groupe a le nombre le plus élevé

Cette requête implémente ce qui précède:

private string GetMostFrequent(IEnumerable<string> items) 
{ 
    var itemsOrderedByCount = 
     from item in items 
     group item by item into itemGroup 
     orderby itemGroup.Count() descending, itemGroup.Key 
     select itemGroup.Key; 

    return itemsOrderedByCount.FirstOrDefault(); 
} 

La mise en œuvre se lit comme la description de haut niveau, un bel effet de bord de la syntaxe déclarative.Voici une explication rapide de chaque partie:

from item in items 

est comme une déclaration de boucle; item fait référence à la variable de boucle.

group item by item into itemGroup 

Ceci place chaque item dans un groupe en fonction de sa valeur. Ceci compte chaque groupe et les trie de sorte que le plus fréquent soit le premier. S'il y a deux groupes avec le même nombre, la valeur moindre est choisie. (Comme chaque groupe contient toutes les mêmes valeurs, la clé est l'élément compté.)

select itemGroup.Key 

Cela dit que pour chaque groupe, nous voulons juste l'élément compté.

return itemsOrderedByCount.FirstOrDefault(); 

Ce saisit le premier élément de la liste ordonnée (celui avec le nombre le plus élevé). Si la séquence d'origine est vide, null est renvoyé.

Utilisation:

var items = new[] { "BOB", "JOHN", "TOM", "TOM", "TOM" }; 

Assert.AreEqual("TOM", GetMostFrequent(items)); 
4

Vous pouvez facilement le faire avec LINQ si vous pouvez l'utiliser, avec une requête similaire à

names.Distinct().OrderByDescending(s => names.Count(u => u == s))).FirstOrDefault(); 

Il retourne la valeur avec le nombre le plus élevé, ou default(Type) . En cas de comptage équivalent, il retournera le premier avec le compte le plus élevé. Vous pouvez mettre cette méthode dans vos extensions avec des génériques pour un usage général.

class Program 
{ 
    static void Main(string[] args) 
    { 

     IEnumerable<String> names = new String[] { "BOB", 
                "JOHN", 
                "TOM", 
                "TOM", 
                "TOM" }; 
     var res = names.Top(); //returns "TOM" 
    } 
} 

public static class Extensions 
{ 

    public static T Top<T>(this IEnumerable<T> values) 
    { 
     return values.Distinct().OrderByDescending(s => values.Count(u => u.Equals(s))).FirstOrDefault(); 
    } 
} 

Si vous avez besoin de toutes les valeurs qui ont le nombre le plus élevé, comme si votre liste était "BOB", "JOHN", "JOHN", "TOM", "TOM" Je suppose que vous pouvez utiliser cette version à la place afin de revenir à la fois JOHN et TOM:

public static IEnumerable<T> Top<T>(this IEnumerable<T> values) 
    { 
     List<T> ret = new List<T>(); 
     int max = -1; 

     foreach (var val in values.Distinct()) 
     { 
      int count = values.Count(t => t.Equals(val)); 

      if (count >= max) 
      { 
       if (count > max) 
       { 
        ret.Clear(); 
        max = count; 
       } 
       ret.Add(val); //stacks equivalent count, if applicable 
      } 
     } 

     return ret; 
    }