asp.net запрос c # Linq для получения суммы каждого значения столбца

#c# #asp.net #.net #arrays #linq

#c# #asp.net #.net #массивы #linq

Вопрос:

У меня есть следующий массив объекта TrackerReport:

 public class TrackerReport : TrackerMilestone
{
    public long Views { get; set; }

    public override string ToString()
    {
        return "Id="   MilestoneId   " | Name="   Name   " | Views = "   Views;
}
 

Я также публикую родительский класс, чтобы лучше объяснить:

 public class TrackerMilestone
{
    public int MilestoneId { get; set; }
    public int CampaignId { get; set; }       
    public string Name { get; set; }
    public int? SortIndex { get; set; }
    public string Url { get; set; }
    public bool IsGoal { get; set; }
    public bool IsPartialGoal { get; set; }
    public int? ParentMilestoneId { get; set; }
    public int? ViewPercent { get; set; }

    public override string ToString()
    {
        return "Id="   MilestoneId   " | Name="   Name   " | Url = "    Url;
    }
}
 

Чтобы оно отображалось так более или менее:

 ID    Name    Url    Counter
1      A     ab.com     5
2      N     ac.com     2
 

И у меня есть список этого объекта, который я заполняю таким образом:

 var trackerReportList = new List<TrackerReport[]>();
foreach (var trackerItem in trackerChildren)
{
    //currentReport is the array of TrackerReport TrackerReport[] above mentioned
    var currentReport = GetReportForItem(GetFromDate(), GetToDate(), trackerItem.ID,
                                                 FindCampaignId(trackerItem));
    trackerReportList.Add(currentReport);    
}
 

Все записи имеют одинаковые значения, но счетчик, так что, например:
список 1:

 ID    Name    Url    Counter
1      A     ab.com     5
2      N     ac.com     2
 

список 2:

 ID    Name    Url    Counter
1      A     ab.com     17
2      N     ac.com     28
 

Моя цель — сгенерировать один TrackerReport[] с суммой значений счетчика в качестве счетчика.

TrackerReport[]:

 ID    Name    Url    Counter
1      A     ab.com     22
2      N     ac.com     30
 

Есть ли какой-либо запрос Linq для этого? Или, если вы придумаете лучшее решение, просто опубликуйте его.

Заранее спасибо!

Комментарии:

1. ну, трекер — это точно парень: P

Ответ №1:

Вот как это сделать с помощью Linq.

  1. Во-первых, вы хотите получить данные в одном списке, а не в List списке Arrays . Мы можем сделать это с SelectMany помощью .

    trackerReportList.SelectMany(c => c) сглаживается List<TrackerReport[]>() в IEnumerable<TrackerReport>

  2. Затем вы группируете объекты по соответствующему столбцу / столбцам
     group p by new {
                ID = p.MilestoneId, 
                Name = p.Name,
                Url = p.Url} into g
     
  3. Наконец, вы проецируете группировку в нужный нам формат. Counter получается Adding Views для коллекции объектов в каждой группе.
     select new {
        ...
    };
     

Собрать все это вместе:

 var report = from p in trackerReportList.SelectMany(c => c) 
             group p by new {
                ID = p.MilestoneId, 
                Name = p.Name,
                Url = p.Url} into g
             select new {
                ID = g.Key.ID,
                Name = g.Key.Name,
                Url = g.Key.Url,
                Counter = g.Sum(c => c.Views)
            };
 

Комментарии:

1. Tnx много, я видел это решение после того, как уже попробовал другое

Ответ №2:

Это lambda expression для вашего запроса.

 TrackerReport[] trackerInfoList = trackerReportList
    .SelectMany(s => s)
    .GroupBy(g => g.MilestoneId)
    .Select(s =>
                {
                    var trackerReportCloned = Clone<TrackerReport[]>(s.First());
                    trackerReportCloned.Views = s.Sum(su => su.Views);
                    return trackerReportCloned;
                })
    .ToArray();
 

Если вы заметили, я использовал Clone<T>(T) метод глубокого клонирования object по сгруппированному значению. Я мог бы сделать это, скопировав каждое свойство, но я нашел это самым простым.

Это и есть Clone() метод:

 public static T Clone<T>(T source)
{
    if (!typeof(T).IsSerializable)
    {
        throw new ArgumentException("The type must be serializable.", "source");
    }

    // Don't serialize a null object, simply return the default for that object
    if (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }

    IFormatter formatter = new BinaryFormatter();
    Stream stream = new MemoryStream();
    using (stream)
    {
        formatter.Serialize(stream, source);
        stream.Seek(0, SeekOrigin.Begin);
        return (T)formatter.Deserialize(stream);
    }
}
 

Прежде чем продолжить, вам необходимо установить [Serializable] атрибут для ваших DTO объектов.

 [Serializable]
public class TrackerReport : TrackerMilestone
{
    .
    .
    .
}

[Serializable]
public class TrackerMilestone
{
    .
    .
    .
}
 

Надеюсь, это то, что вы ищете.

Ответ №3:

Я бы сделал это следующим образом: 1) Пусть TrackerReports[] resultList это массив глубоких копий trackerReportList[0] элементов 2) Как я понимаю, свойство Views соответствует столбцу счетчика. Если это так, то

 foreach(report in resultList)
{
    report.Views = trackerReportList.Sum(reports => reports.Single(r => r.MilestoneId == report.MilestoneId).Views);
}