如何按属性批量收集,递归

时间:2013-12-04 01:02:16

标签: linq parallel-processing

我正在尝试提出一种算法,该算法给出了带子项的对象集合,“AlarmCount”属性会将集合拆分为平行批处理以便并行处理。

例如:

    public class AlarmElement
    {
       public int Id { get; set; }
       public IEnumerable<AlarmElement> Children { get; set; }
       public int AlarmCount { get; set; }
    }

    var list = new List<AlarmElement>();
    list.Add(new AlarmElement { Id = 1, AlarmCount = 10, Children = new List<AlarmElement> { new AlarmElement { AlarmCount = 10 } } });
    list.Add(new AlarmElement { Id = 2, AlarmCount = 5, Children = new List<AlarmElement> { new AlarmElement { AlarmCount = 5 } } });
    list.Add(new AlarmElement { Id = 3, AlarmCount = 5, Children = new List<AlarmElement> { new AlarmElement { AlarmCount = 5 } } });

该函数将返回2个集合,一个带有AlarmElement 1(20个警报),另一个带有AlarmElement 2和3(20个警报)。

有关实现这一目标的最有效方法的任何想法?

1 个答案:

答案 0 :(得分:0)

好问题:)

我已经为这个问题编写了一个启动解决方案,将第一个列表拆分为2个列表,每个列表的总量接近另一个列表。

    public class AlarmElement
    {
        public int Id { get; set; }
        public IEnumerable<AlarmElement> Children { get; set; }
        public int AlarmCount { get; set; }
    }

    class Grouped
    {
        public int Total { get; set; }
        public AlarmElement Alarm { get; set; }
    }

    int Count(AlarmElement element)
    {
        if (element == null) return 0;

        return element.AlarmCount + Count(element.Children);
    }

    int Count(IEnumerable<AlarmElement> elements)
    {
        if (elements == null || elements.Count() == 0)
        {
            return 0;
        }

        int count = 0;

        foreach (AlarmElement alarm in elements)
        {
            count += Count(alarm);
        }

        return count;
    }

所以那些只是一个辅助类和2个计算每个AlarmElement子项的方法。

Split方法采用一个集合,它是平均值,只是将它分成两个平均值的集合:

    private void Split(IEnumerable<Grouped> alarms, double avg, 
        out IEnumerable<AlarmElement> firstAlarms, 
        out IEnumerable<AlarmElement> secondAlarms)
    {
        firstAlarms = 
            alarms
            .Where(a => a.Total < avg)
            .Select(a => a.Alarm);

        secondAlarms =
             alarms
            .Where(a => a.Total > avg)
            .Select(a => a.Alarm);
    }

这是您发送初始警报列表的入口点:

    public IEnumerable<IEnumerable<AlarmElement>> GetBalanced(List<AlarmElement> list)
    {            
        var alarms =
            list
            .Select(a => new Grouped { Total = Count(a), Alarm = a });

        var avg = alarms.Average(a => a.Total);

        var result = new List<IEnumerable<AlarmElement>>();

        IEnumerable<AlarmElement> firstAlarms;               

        IEnumerable<AlarmElement> secondAlarms;

        Split(alarms, avg, out firstAlarms, out secondAlarms);

        result.Add(firstAlarms);
        result.Add(secondAlarms);

        return result;
    }

正如我所提到的,它只是一个启动器,因为您可以通过在每个部件上递归使用Split方法来进一步增强它,直到您满意为止。