Найти частоту значений в массиве или XML (C #) - PullRequest
2 голосов
/ 17 декабря 2008

У меня есть лента XML (которую я не контролирую), и я пытаюсь выяснить, как определить объем определенных значений атрибутов в документе.

Я также анализирую XML и разделяю атрибуты на массивы (для других функций)

Вот пример моего XML

<items>
<item att1="ABC123" att2="uID" />
<item att1="ABC345" att2="uID" />
<item att1="ABC123" att2="uID" />
<item att1="ABC678" att2="uID" />
<item att1="ABC123" att2="uID" />
<item att1="XYZ123" att2="uID" />
<item att1="XYZ345" att2="uID" />
<item att1="XYZ678" att2="uID" />
</items>

Я хочу найти узлы тома на основе каждого значения att1. Значение Att1 изменится. Как только я узнаю частоту значений att1, мне нужно получить значение att2 этого узла.

Мне нужно найти ТОП 4 предметов и вытащить значения их атрибутов.

Все это должно быть сделано в коде C #.

Если бы я использовал Javascript, я бы создал ассоциативный массив и имел бы значение att1, а значение - частоту. Но так как я новичок в c #, я не знаю, как продублировать это в c #.

Поэтому я считаю, что сначала мне нужно найти все уникальные значения att1 в XML. Я могу сделать это используя:

IEnumerable<string> uItems = uItemsArray.Distinct();
// Where uItemsArray is a collection of all the att1 values in an array

Затем я зацикливаюсь на том, как сравнивать каждое уникальное значение att1 со всем документом, чтобы получить объем, хранящийся в переменной, массиве или любом другом наборе данных.

Вот фрагмент, который я использовал:

        XDocument doc = XDocument.Load(@"temp/salesData.xml");
        var topItems = from item in doc.Descendants("item")
                    select new
                    {
                        name = (string)item.Attribute("name"),
                        sku = (string)item.Attribute("sku"),
                        iCat = (string)item.Attribute("iCat"),
                        sTime = (string)item.Attribute("sTime"),
                        price = (string)item.Attribute("price"),
                        desc = (string)item.Attribute("desc")

                    } into node
                    group node by node.sku into grp
                    select new { 
                        sku = grp.Key,
                        name = grp.ElementAt(0).name,
                        iCat = grp.ElementAt(0).iCat,
                        sTime = grp.ElementAt(0).sTime,
                        price = grp.ElementAt(0).price,
                        desc = grp.ElementAt(0).desc,
                        Count = grp.Count() 
                    };

        _topSellers = new SalesDataObject[4];
        int topSellerIndex = 0;
        foreach (var item in topItems.OrderByDescending(x => x.Count).Take(4))
        {
            SalesDataObject topSeller = new SalesDataObject();
            topSeller.iCat = item.iCat;
            topSeller.iName = item.name;
            topSeller.iSku = item.sku;
            topSeller.sTime = Convert.ToDateTime(item.sTime);
            topSeller.iDesc = item.desc;
            topSeller.iPrice = item.price;
            _topSellers.SetValue(topSeller, topSellerIndex);
            topSellerIndex++;
        } 

Спасибо за вашу помощь!

Ответы [ 3 ]

4 голосов
/ 17 декабря 2008

Вы используете .NET 3.5? (Похоже, это основано на вашем коде.) Если так, я подозреваю, что это довольно легко с LINQ to XML и LINQ to Objects. Однако, боюсь, из вашего примера не ясно, чего вы хотите. Все ли значения с одинаковым att1 и имеют одинаковое att2? Если это так, это что-то вроде:

var results = (from element in items.Elements("item")
              group element by element.Attribute("att1").Value into grouped
              order by grouped.Count() descending
              select grouped.First().Attribute("att2").Value).Take(4);

Я не проверял это, но я думаю это должно работать ...

  • Мы начинаем со всех элементов элемента
  • Мы группируем их (все еще как элементы) по значению att1
  • Мы сортируем группы по размеру в порядке убывания, поэтому самая большая из них является первой
  • Из каждой группы мы берем первый элемент, чтобы найти его значение att2
  • Мы берем четверку лучших из этих результатов
1 голос
/ 17 декабря 2008

Вы можете использовать LINQ / XLINQ для достижения этой цели. Ниже приведен пример консольного приложения, которое я только что написал, поэтому код может быть не оптимизирован, но он работает.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;

namespace FrequencyThingy
{
    class Program
    {
        static void Main(string[] args)
        {
            string data = @"<items>
                            <item att1=""ABC123"" att2=""uID"" />
                            <item att1=""ABC345"" att2=""uID"" />
                            <item att1=""ABC123"" att2=""uID"" />
                            <item att1=""ABC678"" att2=""uID"" />
                            <item att1=""ABC123"" att2=""uID"" />
                            <item att1=""XYZ123"" att2=""uID"" />
                            <item att1=""XYZ345"" att2=""uID"" />
                            <item att1=""XYZ678"" att2=""uID"" />
                            </items>";
            XDocument doc = XDocument.Parse(data);
            var grouping = doc.Root.Elements().GroupBy(item => item.Attribute("att1").Value);

            foreach (var group in grouping)
            {
                var groupArray = group.ToArray();
                Console.WriteLine("Group {0} has {1} element(s).", groupArray[0].Attribute("att1").Value, groupArray.Length);
            }

            Console.ReadKey();
        }
    }
}
1 голос
/ 17 декабря 2008

Если у вас есть значения, вы сможете использовать LINQ GroupBy ...

        XDocument doc = XDocument.Parse(xml);
        var query = from item in doc.Descendants("item")
                    select new
                    {
                        att1 = (string)item.Attribute("att1"),
                        att2 = (string)item.Attribute("att2") // if needed
                    } into node
                    group node by node.att1 into grp
                    select new { att1 = grp.Key, Count = grp.Count() };

        foreach (var item in query.OrderByDescending(x=>x.Count).Take(4))
        {
            Console.WriteLine("{0} = {1}", item.att1, item.Count);
        }
...