使用LINQ查询列表的列表链

本文关键字:列表 查询 LINQ 使用 | 更新日期: 2023-09-27 18:17:21

我正在使用一个名为SDMX的XML标准。它相当复杂,但我将使它尽可能简短。我接收到一个叫做CategoryScheme的物体。这个对象可以包含若干个Category,每个Category可以包含更多的Category,以此类推,这个链可以是无限的。每个Category都有一个唯一的ID。

通常每个Category包含很多类别。与此对象一起,我正在接收一个数组,其中包含指示特定Category嵌套位置的ID列表,然后我正在接收该类别的ID。我需要做的是创建一个对象来维护Category对象的层次结构,但是每个Category必须有,只有一个子,并且该子必须是导致特定Category的树中的一个。所以我有一个想法,但为了做到这一点,我应该生成 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;
    }
}

谢谢你的帮助。

使用LINQ查询列表的列表链

所以,正如我在评论中已经说过的,构建递归函数应该可以解决这个问题。如果你不熟悉它,你可以在这里找到一些关于c#递归的基本信息。

方法可以像这样:

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中)。

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

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

IEnumerable<int> test = new List<int>() { 1, 2, 3, 4 , 1 };
test.ToList().RemoveAll(a => a != 1);

编辑:

所以这是下面讨论之后的另一种可能的方式。不知道你真正需要的是什么,所以试试吧。

    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调用而丢失数据。它的意思是"不顾一切地抓住第一件东西"。我知道你说你只有一件可以,但是……:)

请在评论中告诉我这是否适用于你。

//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);
    }
}

感谢那些试图帮助我的人,但我自己用一种更容易的方法解决了这个问题。

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

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