简体   繁体   English

在 C# 中使用 LINQ 在列表列表中查找和更新值

[英]Using LINQ in C# to find and update values in a list of lists

I have three classes listed below:我有下面列出的三个类:

class OuterObjects
{
    string name;
    List<InnerObjects> myList;
}

class InnerObjects
{
    float small;
    float big;
}

class CorrectValues
{
    string name;
    float correctValue;
}

So my code runs through everything and gets to a point where I have all the OuterObjects and InnerObjects mapped and then seperately I load up CorrectValues with a database call.因此,我的代码遍历所有内容并到达一个点,即我已映射所有 OuterObjects 和 InnerObjects,然后分别使用数据库调用加载 CorrectValues。

So I have:所以我有:

List<OuterObjects> theList = [loaded up with many names and InnerObjects]

So I need to write something that will go through the OuterObjects and into the InnerObjects and find all small > big and fix them.所以我需要写一些东西,将 go 通过 OuterObjects 并进入 InnerObjects 并找到所有小>大并修复它们。 The (float) big will be replaced by the correctValue where the name in CorrectValues and OuterObjects match. (float) big 将替换为 CorrectValues 和 OuterObjects 中的名称匹配的正确值。 Name is can also appear more than once in each of it's objects, but the correctValue will always match the same name (ie if there are 2 names of "Bob" in CorrectValues then the correctValue will be the same).名称也可以在它的每个对象中多次出现,但正确的值将始终匹配相同的名称(即,如果在正确值中有 2 个“鲍勃”的名称,那么正确的值将是相同的)。

I have something like:我有类似的东西:

outerObjects.Select(q => q.InnerObjects).Where(q => (q.small > q.big) ... then update the big with the correctValue

Is there a good, elegant way to do this?有没有一种好的、优雅的方式来做到这一点? I don't want to use forEach statements either.我也不想使用 forEach 语句。

I understand you don't want to use forEach loops for this, but in this case, it might be appropriate.我知道您不想为此使用 forEach 循环,但在这种情况下,它可能是合适的。

For example, you could do the following:例如,您可以执行以下操作:

public void UpdateTheList(List<OuterObjects> theList) {
    theList.ForEach(x => {
       x.name = "John";
       x.myList.ForEach(x => {
       x.big = updateBigDbCall();
       x.small = updateSmallDbCall();
       });
    });
}

where you are updating big and small in separate database calls and updating all names to "John".您在单独的数据库调用中更新大小,并将所有名称更新为“John”。

The other way this could be done is using a nested select statement:另一种方法是使用嵌套的 select 语句:

theList = theList.Select(e => {
   e.name = "John";
   e.myList.Select(x => {
      x.big = updateBigDbCall();
      x.small = updateSmallDbCall();
      return x;
    }).ToList();
    return e;
}).ToList();

To be honest I prefer to break these operations down into separate short methods doing one thing each.老实说,我更喜欢将这些操作分解为单独的简短方法,每个方法做一件事。

So getting the list of objects to update could be a method ie selecting out of your collection only those items that you need to fix.因此,获取要更新的对象列表可能是一种方法,即从您的集合中仅选择您需要修复的那些项目。

The second method would iterate the collection and make any updates.第二种方法将迭代集合并进行任何更新。

The third method would be the work required to do the update.第三种方法是进行更新所需的工作。

You could wrap these three methods in an upper method UpdateList for example and it would be clear for any other developer to understand the purpose of the code.例如,您可以将这三个方法包装在上层方法 UpdateList 中,任何其他开发人员都可以清楚地了解代码的用途。

If you're going to be modifying objects within a Linq statement (such as within a Select() that the blog you link to shows), you're going to have a bad time.如果您要在 Linq 语句中修改对象(例如在您链接到的博客中显示的Select()中),那么您将度过一段糟糕的时光。

Select() is to project objects, which means that you're going to map them into new objects. Select()投影对象,这意味着您将 map 将它们转换为新对象。

var customerNames = allCustomers.Select(c => c.Firstname + " " + c.Lastname).ToList();

If you're going to abuse that method to modify the objects that pass by it, you're not doing pure, functional, set-oriented programming anymore, and that's not what Linq is meant for.如果您要滥用该方法来修改通过它的对象,那么您将不再进行纯粹的、功能性的、面向集合的编程,这不是 Linq 的目的。 This will also modify the items in the source collection, which usually is not what you want, and is going to cause headaches later on.这也会修改源集合中的项目,这通常不是您想要的,并且会在以后引起头疼。

Where() is to filter objects from a collection. Where()是从集合中过滤对象。 You're going to reduce them to a new collection, like so:您要将它们减少到一个新的集合,如下所示:

var activeCustomers = allCustomers.Where(c => c.IsActive).ToList();

A rough, idiomatic way to do what you want using foreach() would look like this:使用foreach()做你想做的事情的粗略、惯用的方法如下所示:

foreach (var outerObject in theList)
{
    var correction = correctValues.FirstOrDefault(c => c.Name == outerObject.Name);
    if (correction == null)
    {
        // We can't fix this outerObject
        continue;
    }
    
    // Look, Linq!
    foreach (var innerObject in outerObject.InnerObjects.Where(o => o.Small > o.Big))
    {
        // Here you're assuming that the correction will have the correct value
        innerObject.Big = correction.CorrectValue;
    }
}

Now sure, you could translate this to something with a .Where() and a .Join() , but that won't make the code any more readable or performant, and you will still have to do object manipulation to modify each outerObject.InnerObject where Small > Big , and you don't want to do that within a Select() , so you're going to have to call .Where(...).ToList().ForEach(/* do the update */) anyway, and the code within that ForEach() action will more or less be exactly the same as in the inner foreach() in my code above.现在可以肯定,您可以将其转换为带有.Where().Join()的东西,但这不会使代码更具可读性或性能,并且您仍然需要进行 object 操作来修改每个outerObject.InnerObject where Small > Big ,并且您不想在Select()中执行此操作,因此您将不得不调用.Where(...).ToList().ForEach(/* do the update */)无论如何,并且该ForEach()操作中的代码将或多或少与我上面代码中的内部foreach()中的代码完全相同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM