使用 LINQ 查询列表列表链

Querying a chain of list of lists with LINQ

我正在使用一个名为 SDMX 的 XML 标准。这相当复杂,但我会尽可能简短。我收到一个名为 CategoryScheme 的 object。这个object可以包含多个Category,每个Category可以包含更多的Category,以此类推,链条可以是无限的。每个 Category 都有一个唯一的 ID。

通常每个 Category 包含很多类别。与此 object 一起,我收到一个数组,其中包含指示特定 Category 嵌套位置的 ID 列表,然后我收到该类别的 ID。 我需要做的是创建一个 object 来维护 Category object 的层次结构,但是每个 Category 必须只有 [=31] =] 一个 child 并且 child 必须是导致特定 Category 的树之一。 所以我有了一个主意,但为了做到这一点,我应该在一个循环内 generate LINQ 查询,但我不知道如何做到这一点。有关我想尝试的更多信息在代码中进行了评论

我们来看代码:

public void RemoveCategory(ArtefactIdentity ArtIdentity, string CategoryID, string CategoryTree)
{
    try
    {
        WSModel wsModel = new WSModel();

        // Prepare Art Identity and Array

        ArtIdentity.Version = ArtIdentity.Version.Replace("_", ".");
        var CatTree = JArray.Parse(CategoryTree).Reverse();

        // Get Category Scheme

        ISdmxObjects SdmxObj = wsModel.GetCategoryScheme(ArtIdentity, false, false);

        ICategorySchemeMutableObject CatSchemeObj = SdmxObj.CategorySchemes.FirstOrDefault().MutableInstance;

        foreach (var Cat in CatTree)
        {
            // The cycle should work like this.
            // At every iteration it must delete all the elements except the correct one
            // and on the next iteration it must delete all the elements of the previously selected element
            // At the end, I need to have the CatSchemeObj full of the all chains of categories.

            // Iteration 1...
            //CatSchemeObj.Items.ToList().RemoveAll(x => x.Id != Cat.ToString());

            // Iteration 2...
            //CatSchemeObj.Items.ToList().SingleOrDefault().Items.ToList().RemoveAll(x => x.Id != Cat.ToString());

            // Iteration 3...
            //CatSchemeObj.Items.ToList().SingleOrDefault().Items.ToList().SingleOrDefault().Items.ToList().RemoveAll(x => x.Id != Cat.ToString());

            // Etc...
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

感谢您的帮助。

已编辑:

下面是讨论后的另一种可能方法。 不确定您真正需要什么,请尝试一下。

    int counter = 0;
    var list = CatSchemeObj.Items.ToList();
    //check before you call it or  you will get an error
    if(!list.Equals(default(list)))
    {
        while(true)
        {
            var temp = list.Where(x => CatTree[counter++] == x.Id); // or != ? play with it .
            list = temp.Items.ToList().SingleOrDefault();

            if(list.Equals(default(list))
            {
                break;
            }
        }
    }

我刚刚将您的问题翻译成 2 个解决方案,但我不确定您是否会因为 SingleOrDefault 调用而丢失数据。意思是'Grab the first item regardless of everything'。我知道你说你只有 1 件商品没问题,但仍然......:)

如果这对你有用,请在评论中告诉我。

//solution 1
// inside of this loop check each child list if empty or not
foreach (var Cat in CatTree)
{
    var list = CatSchemeObj.Items.ToList();
    //check before you call it or  you will get an error
    if(!list.Equals(default(list)))
    {
        while(true)
        {
            list.RemoveAll(x => x.Id != Cat.ToString());
            list = list.ToList().SingleOrDefault();

            if(list.Equals(default(list))
            {
                break;
            }
        }
    }
}

//solution 2

foreach (var Cat in CatTree)
{
    var list = CatSchemeObj.Items.ToList();
    //check before you call it or  you will get an error
    if(!list.Equals(default(list)))
    {
        CleanTheCat(cat, list);
    }
}

//use this recursive function outside of loop because it will cat itself
void CleanTheCat(string cat, List<typeof(ICategorySchemeMutableObject.Items) /*Place here whatever type you have*/> CatSchemeObj)
{
    CatSchemeObj.RemoveAll(x => x.Id != cat);

    var catObj = CatSchemeObj.Items.ToList().SingleOrDefault();

    if (!catObj.Equals(default(catObj)){
        CleanTheCat(cat, catObj);
    }
}

因此,正如我在评论中所说,构建递归函数应该可以解决问题。如果您是新手,可以在 C# here.

中找到有关递归的一些基本信息

该方法可能如下所示:

private void DeleteRecursively(int currentRecursionLevel, string[] catTree, ICategorySchemeMutableObject catSchemeObj) 
{
    catSchemeObj.Items.ToList().RemoveAll(x => x.Id != catTree[currentRecursionLevel].ToString());
    var leftoverObject = catSchemeObj.Items.ToList().SingleOrDefault();
    if(leftoverObject != null) DeleteRecursively(++currentRecursionLevel, catTree, leftoverObject);
}

之后你可以在你的主方法中调用这个方法,而不是循环:

    DeleteRecursively(0, CatTree, CatSchemeObject);

但正如我所说,请记住,在循环中调用方法对我来说似乎毫无意义,因为你已经清除了树,除了一个剩余的路径,所以用同一棵树调用方法,但另一个类别将导致空树(在 CatSchemeObject 中)。

小心! 我现在注意到的另一件事:调用列出您的项目 属性 然后删除条目,将 NOT 影响您的源对象,因为 ToList 正在生成一个新对象。它保留引用的原始对象,但删除只会影响列表。所以你必须将结果列表写回你的Items属性,或者想办法直接在Items对象中删除。 (假设它是一个 IEnumerable 而不是一个具体的集合类型,你应该把它写回去)。

用这个简单的例子试试看,你会发现原来的列表没有被修改。

IEnumerable<int> test = new List<int>() { 1, 2, 3, 4 , 1 };

test.ToList().RemoveAll(a => a != 1);

感谢任何试图提供帮助的人,但我自己以更简单的方式解决了它。

我只是将完整的 CategoryScheme 对象发送到将其转换为 XML 格式的方法,然后只需一行就可以了:

XmlDocument.Descendants("Category").Where(x => !CatList.Contains(x.Attribute("id").Value)).RemoveIfExists();