开发者

Compare/count values in a System.Collections.ArrayList

I'm scrubbing 5 files for a specific value. I dont anticipate any different values, BUT since this is for my own educational purposes, I would like the application to count, compare and print the most popular value.

for example:

ArrayList arrName = new ArrayList();
arrName.Add("BOB")
arrName.Add("JOHN")
arr开发者_高级运维Name.Add("TOM")
arrName.Add("TOM")
arrName.Add("TOM")

The result I would like is going to be TOM but being a novice, I really dont know how to move forward.

Any thoughts, suggestions or examples are greatly appreciated. Thank you.


You can easily do this with LINQ if you can use it, with a query similar to

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

It will return the value with the highest count, or default(Type). In cases of equivalent counts, it will return the first one with he highest count. You can put that method in your extensions with generics for general usage.

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();
    }
}

If you need all values that have the highest count, like if your list was "BOB", "JOHN", "JOHN", "TOM", "TOM" I guess you could use this version instead in order to return both JOHN and 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;
    }


You didn't specify the version of .Net / C# you are using, so I'll treat this for each version of C#: v1, v2 and 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);


This is the kind of task for which LINQ is well-suited.

First, let's define what we're doing:

  1. Group the items by value
  2. Count each group
  3. Return the item whose group has the highest count

This query implements the above:

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();
}

The implementation reads very much like the high-level description, a nice side-effect of the declarative syntax. Here is a quick explanation of each part:

from item in items

is like a loop declaration; item refers to the loop variable.

group item by item into itemGroup

This puts each item in a group based on its value.

orderby itemGroup.Count() descending, itemGroup.Key

This counts each group and sorts them such that the most frequent is first. If there are two groups with the same count, the lesser value is chosen. (As each group contains all the same values, the key is the counted item.)

select itemGroup.Key

This says that for each group, we just want the counted item.

return itemsOrderedByCount.FirstOrDefault();

This grabs the first item in the ordered list (the one with the highest count). If the original sequence is empty, null is returned.

Usage:

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

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


You can use a Dictionary (.NET 2.0+) to hold the repeated count of each value:

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);

If you're using C# 3.0 (.NET 3.5 +) then use:

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");


    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);
    }


To move through the loop, you can use a foreach:

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

And to count the values, you can use a Hashtable, which maps keys to values. The key can be a name, and the value can be how many times you've seen that name in the list.

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);
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜