使用 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();
我正在使用一个名为 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();