【问题标题】:C# Finding elements from a list to match each elements from another listC# 从列表中查找元素以匹配另一个列表中的每个元素
【发布时间】:2016-05-25 09:42:36
【问题描述】:

我的想法是我有一个对象GrossPanel,它有一个属性GrossPanel.PanelList,其中包含Panelobjects 的列表;List<Panel>

每个Panelobject 都具有double Panel.Prod_Width 类型的属性

我想做的是将input中的每个Panel.Prod_Widthtemplate.PanelList中的每个Panel.Prod_Width匹配

找到匹配项后,来自inputlist 的Panel 被放入一个新的GrossPanelobject 并从input 中删除。如果找到完整的匹配集,则将生成的 GrossPanel 添加到 _Returnlist 并重复每个操作,直到用完 input 列表。

例子:

假设input包含9个元素(Panel0-Panel8)和template包含2个元素(temp0-temp1)

  • Panel0-Panel3 的 Prod_Width = 200
  • Panel4-Panel7 的 Prod_Width = 300
  • Panel8 的 Prod_Width = 400
  • temp0 的 Prod_Width = 200,temp1 的 Prod_Width = 300

这应该创建 4 个GrossPanelobjects,GP0-GP3

  • GP0 应包含 Panel0 和 Panel4
  • GP1 应包含 Panel1 和 Panel5
  • GP2 应该包含 Panel2 和 Panel6
  • GP3 应包含 Panel3 和 Panel7

Panel8 无法使用

这是我为此编写的代码。它可以工作,但速度很慢。 有没有办法更有效地做到这一点?我试图做一个foreach 循环并沿途从input 中删除元素,但它不起作用。相反,我使用Index_index 跳过input 中的“使用”元素

private static List<GrossPanel> Match (List<Panel> input, GrossPanel template)
    {
        List<Panel> _template = template.PanelList.OrderBy(panel => panel.Prod_Width).ToList();
        List<Panel> _input = input.OrderBy(panel => panel.Prod_Width).ToList();
        List<GrossPanel> _Returnlist = new List<GrossPanel>();
        List<int> indexlist = new List<int>(); // list of used indexes
        int Index = 0; //counting the panels you have checked
        while (Index < _input.Count)
        {
            int _index = 0;
            GrossPanel Grosspanel = new GrossPanel();
            for (int templateindex = 0; templateindex < _template.Count(); templateindex++)
            {
                for (int inputindex = _index; inputindex < _input.Count(); inputindex++)
                {
                    if ((!indexlist.Contains(inputindex)) && (_template.ElementAt(templateindex).Prod_Width == _input.ElementAt(inputindex).Prod_Width))
                    {
                        indexlist.Add(inputindex);
                        Grosspanel.AddNetPanel(input.ElementAt(inputindex));
                        _index = indexlist.Last(); // 
                        Index++;
                        break;
                    }
                }
            }
            if (Grosspanel.NetPanelCount == _template.Count()) _Returnlist.Add(Grosspanel);
            else if (Grosspanel.NetPanelCount != _template.Count()) Index = _input.Count;
        }
        return _Returnlist;
    }

好的……

我尝试使用IEnuberableyield return 来加快速度。我现在的问题是,当我在 input 中找到匹配项时,我似乎无法在下一次迭代中将其从 input 中删除。

这里是代码

private static IEnumerable<GrossSteniPanel> Match (IEnumerable<Panel> input, GrossPanel template, List<Panel> usedpanels, int index)
    {
        GrossPanel Grosspanel;
        List<Panel> _usedpanels = new List<Panel>();
        IEnumerable<Panel> _input = input;
        _input = _input.Except(usedpanels);
        if (index < 0 | (_input.Count() == 0)) yield return Grosspanel = new GrossPanel();
        else
        { 
            foreach (Panel p in _input)
            {
                if (p.Prod_Width == template.NetPanelList.ElementAt(index).Prod_Width)
                {
                    _usedpanels.Add(p);
                    _input = _input.Except(_usedpanels);
                    foreach (GrossPanel panel in Match (_input, template, usedpanels, index - 1))
                    {
                        Grosspanel = panel;
                        Grosspanel.AddNetPanel(p);
                        yield return Grosspanel;
                    }
                }
            }
        }
    } 

我错过了什么??

【问题讨论】:

  • 想法:创建两个查找表,一个用于宽度->模板,一个用于宽度->面板。然后 foreach 输入列表中的面板,检查哪个模板适用(第二次查找),然后检查哪个面板适合另一个插槽(第一次查找)。然后从第一次查找中删除找到的面板并将创建的总面板添加到结果列表中。这应该比 O(N^3) 低 O(N),但你不会得到最好的“填充率”,也就是说,如果有两个模板适合面板并且只有一个有一个面板可用于它的第二个位置,如果选择正确的位置是幸运的。
  • 您的template.PanelList 是否包含带有重复Prod_Width 的面板?
  • 是的,template.PanelList 可以包含具有重复 Prod_Width 的面板,但它始终是 input 列表中面板的组合。最终目标是从input 中找到具有一定长度的每个唯一组合。(这是template,我有生成此代码的代码)然后在input 中找到所有匹配的面板组合模板(原帖中的代码是这样做的,但是太慢了……)然后我会检查哪个template是在input中堆叠面板最有效的方法该函数会运行多次,所以我想要让它跑得更快
  • @Haukinger - 如何从 Element&lt;T&gt; 中删除 Element&lt;T&gt; ?据我了解,我必须从输入列表中删除该元素,然后根据现在更小的输入列表创建一个新的查找。
  • 反向查找怎么样?使用它来获取密钥,使用它从常规查找中删除,然后从反向查找中删除。您可以通过包装两个常规字典来创建一个 IDictionary 实现(按值删除恒定时间,而不仅仅是按键)。

标签: c# list sorting match


【解决方案1】:

我的建议:

  • 尽量让您的代码更易于阅读和理解(在 99% 的情况下,这比性能更重要)。
  • 命名变量时使用一种约定。我在“Match”方法的前 5 行中发现了 4 种不同的命名约定。
  • 在局部变量名前使用下划线不好。
  • 尝试为“Match”方法编写测试。

重构示例(名称“Match”更改为“BuildGrossPanelList”):

static IList<GrossPanel> BuildGrossPanelList(List<Panel> input, GrossPanel template)
{
    var templatePanels = template.PanelList
        .OrderBy(panel => panel.Width);

    var inputPanels = input
        .OrderBy(panel => panel.Width)
        .ThenBy(panel => panel.Id);        
    // If `input` can have elements with the same `panel.Width` 
    // and you want to always retun the same result then the sorting has to be extend.

    var templatesWithMatchingPanels = templatePanels
        .ToDictionary(
            tp => tp,
            tp => inputPanels.Where(p => p.Width == tp.Width).ToList());

    return GetGrossPanels(templatesWithMatchingPanels);
}

static IList<GrossPanel> GetGrossPanels(Dictionary<Panel, List<Panel>> templatesWithMatchingPanels)
{
    var result = new List<GrossPanel>();
    while(AnyNotUsedPanelExistsForEveryTemplate(templatesWithMatchingPanels))
    {
        result.Add(CreateNewGrossPanel(templatesWithMatchingPanels));
    }
    return result;
}

static bool AnyNotUsedPanelExistsForEveryTemplate(Dictionary<Panel, List<Panel>> templatesWithMatchingPanels)
{
    return templatesWithMatchingPanels.All(entry => entry.Value.Any());
}

static GrossPanel CreateNewGrossPanel(Dictionary<Panel, List<Panel>> templatesWithMatchingPanels)
{
    var result = new GrossPanel();
    foreach(var templatePanelEntry in templatesWithMatchingPanels)
    {
        var firstMatchingPanel = GetAndRemoveFirst(templatePanelEntry.Value);
        result.AddNetPanel(firstMatchingPanel);
    }
    return result;
}

static Panel GetAndRemoveFirst(IList<Panel> panels)
{
    var result = panels.First();
    panels.RemoveAt(0);
    return result;
}

如果“Match”方法是一个大类的一部分,至少把上面的代码放在一个嵌套类中。考虑在单独的文件中创建一个新类。这个新类中的方法不必是静态的。

【讨论】:

  • 我在声明 var templatesWithMatchingPanels 时收到以下错误,已添加具有相同密钥的项目。这是因为模板包含宽度相同的面板吗?
  • 异常告诉你问题出在哪里。字典中的键必须是唯一的。在这种情况下,键是对 Panel 对象的引用。例如,当 template.PanelList[0] 和 template.PanelList[1] 引用同一个 Panel 对象时,您将收到此错误。
  • 您的 Panel 类也有可能实现 System.IEquatable 接口。如果是这种情况,则使用 E​​quals(T) 方法来确定键是否相等。
  • 模板是通过首先提取input 中的每个不同面板(基于Prod_Width 属性)生成的,然后,从这些不同的面板中,我生成一定长度的所有组合(参见stackoverflow.com/questions/25824376/…) 所以,如果我有两个不同的宽度并且需要一个带有 4 个 NetPanel 的 GrossPanel,我将在模板中得到至少两个相同的面板。在大多数情况下,我最终会在模板中使用相同的面板..
猜你喜欢
  • 1970-01-01
  • 2020-07-07
  • 1970-01-01
  • 2018-05-13
  • 1970-01-01
  • 2023-03-19
  • 1970-01-01
  • 2023-02-01
  • 1970-01-01
相关资源
最近更新 更多