Сравнить / посчитать значения в System.Collections.ArrayList
Я вычищаю 5 файлов для определенного значения. Я не ожидаю каких-либо других значений, НО, поскольку это для моих собственных образовательных целей, я хотел бы, чтобы приложение подсчитывало, сравнивало и печатало наиболее популярные значения.
например:
ArrayList arrName = new ArrayList();
arrName.Add("BOB")
arrName.Add("JOHN")
arrName.Add("TOM")
arrName.Add("TOM")
arrName.Add("TOM")
Результатом, который я хотел бы, будет TOM, но, будучи новичком, я действительно не знаю, как двигаться дальше.
Любые мысли, предложения или примеры с благодарностью. Спасибо.
6 ответов
Вы можете использовать словарь (.NET 2.0+) для хранения повторного количества каждого значения:
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);
Если вы используете C# 3.0 (.NET 3.5 +), используйте:
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");
Вы можете легко сделать это с LINQ, если вы можете использовать его с запросом, аналогичным
names.Distinct().OrderByDescending(s => names.Count(u => u == s))).FirstOrDefault();
Он вернет значение с наибольшим количеством или default(Type)
, В случае эквивалентных подсчетов, он вернет первый с наибольшим количеством. Вы можете поместить этот метод в свои расширения с обобщениями для общего использования.
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();
}
}
Если вам нужны все значения с наибольшим количеством, например, если ваш список "BOB", "JOHN", "JOHN", "TOM", "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;
}
Вы не указали версию.Net / C#, которую вы используете, поэтому я рассмотрю это для каждой версии C#: v1, v2 и 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);
Это та задача, для которой LINQ хорошо подходит.
Во-первых, давайте определимся с тем, что мы делаем:
- Сгруппировать предметы по значению
- Подсчитайте каждую группу
- Вернуть предмет, группа которого имеет наибольшее количество
Этот запрос реализует вышеупомянутое:
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();
}
Реализация выглядит очень похоже на высокоуровневое описание, приятный побочный эффект декларативного синтаксиса. Вот краткое объяснение каждой части:
from item in items
это как объявление цикла; item
ссылается на переменную цикла.
group item by item into itemGroup
Это ставит каждый item
в группе на основе его стоимости.
orderby itemGroup.Count() descending, itemGroup.Key
Это подсчитывает каждую группу и сортирует их так, что наиболее часто встречается первым. Если есть две группы с одинаковым количеством, выбирается меньшее значение. (Поскольку каждая группа содержит все одинаковые значения, ключ - это подсчитанный элемент.)
select itemGroup.Key
Это говорит о том, что для каждой группы мы просто хотим подсчитать элемент.
return itemsOrderedByCount.FirstOrDefault();
Это захватывает первый элемент в упорядоченном списке (тот, который имеет наибольшее количество). Если исходная последовательность пуста, возвращается ноль.
Использование:
var items = new[] { "BOB", "JOHN", "TOM", "TOM", "TOM" };
Assert.AreEqual("TOM", GetMostFrequent(items));
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);
}
Для перемещения по циклу вы можете использовать foreach
:
foreach (string name in arrName) {
Console.WriteLine(i);
}
И чтобы посчитать значения, вы можете использовать Hashtable
, который сопоставляет ключи со значениями. Ключом может быть имя, а значением может быть то, сколько раз вы видели это имя в списке.
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);
}
}