LINQ:新しいオブジェクトを作成せずにオブジェクトを選択して一部のプロパティを変更する


217

新しいオブジェクトを作成してすべてのプロパティを手動で設定せずに、LINQクエリ結果オブジェクトの一部のプロパティを変更したい。これは可能ですか?

例:

var list = from something in someList
           select x // but change one property

7
私の例のように宣言型構文を使用してこれを実現できるようにするJaredParの回答に基づいて、以下の拡張メソッドを書きました。ぜひ
Rob Volk、

3
@RobVolk、リンクは死んでいるように見える-新しいものを手に入れましたか?これがアーカイブです
-LINQ


1
ごめんなさい!ihereは正しいアドレスです。robvolk.com
Rob Volk

回答:


379

クエリ構文が何であるかわかりません。しかし、これは拡張LINQ式の例です。

var query = someList.Select(x => { x.SomeProp = "foo"; return x; })

これは、無名メソッドと式を使用することです。これにより、1つのラムダで複数のステートメントを使用できます。したがって、プロパティを設定し、オブジェクトをこのやや簡潔なメソッドに返すという2つの操作を組み合わせることができます。


4
@Rob、簡単ではない。これを機能させるための構文は...せいぜい判読不能です。var query =リストから選択((Func <Foo>)(()=> {it.x = 42; return it;}))));
JaredPar 2009年

35
「ステートメント本文を持つラムダ式は式ツリーに変換できません」というエラーが表示されます。LINQ to SQLではありません、アドバイスはありますか?
スーリヤ

2
@spiderdevil +1-格差が嫌いではありませんか?コードがLinq to Objectでのみ機能し、Linq to SQL / EFでは機能しない状況に遭遇したのはこれが初めてではありません。これはここでの解決策かもしれませんが、時間がないので、まだ使用しようとしていません。
dyslexicanaboko

11
@suryaこれはまさに、Linq to SQLで試したときに得たメッセージです。ToList()beforeを追加することでうまくいくようです。なぜそれを得るのかわからない。Entity Frameworkの場合、それでも動作しません。
Raziel

5
@suryaがToList()を呼び出すと、実際にはデータがメモリに戻されます。そのため、実際にはLinq to SQLではありません。
2016年

60

すべての要素のプロパティを更新したいだけの場合

someList.All(x => { x.SomeProp = "foo"; return true; })

3
EF(Entity Framework)では、IEnumerableのすべてのオブジェクトのプロパティを置き換えるために、受け入れられた答えが私のために働きました。私の作業コード:var myList = _db.MyObjects.Where(o => o.MyProp == "bar")。AsEnumerable()。Select(x => {x.SomeProp = "foo"; return x;}) ;
firepol 2013年

32

私はこれを好む。他のlinqコマンドと組み合わせることができます。

from item in list
let xyz = item.PropertyToChange = calcValue()
select item

24

これを妨げるLINQの魔法はありません。プロジェクションを使用しないでください。匿名型が返されます。

User u = UserCollection.FirstOrDefault(u => u.Id == 1);
u.FirstName = "Bob"

これにより、実際のオブジェクトが変更されます。

foreach (User u in UserCollection.Where(u => u.Id > 10)
{
    u.Property = SomeValue;
}

私はこれが好きです、私の質問は最初の例では、リストにu> 10が見つからない場合はどうなりますか?nullチェックを追加し、動作するようです。また、LHSはuを+1と名付けました。とても簡潔です。
desaivv 2012

11

標準のクエリ演算子では不可能です。言語統合クエリではなく、言語統合クエリです。ただし、拡張メソッドで更新を非表示にすることができます。

public static class UpdateExtension
{
    public static IEnumerable<Car> ChangeColorTo(
       this IEnumerable<Car> cars, Color color)
    {
       foreach (Car car in cars)
       {
          car.Color = color;
          yield return car;
       }
    }
}

これで、次のように使用できます。

cars.Where(car => car.Color == Color.Blue).ChangeColorTo(Color.Red);

1
基本コレクションを更新する必要はありません。結果のコレクションプロパティを更新してください。
Michael Brennt 2014

4
まあLINQは、構造化の略SQL置き換えるクエリ言語を、それはまだの能力の能力を公開しUPDATEDELETE、とINSERT。だから私は意味論がこの機能を妨げるものであることを主張しません。
KyleMit 2015

5

Where句を使用してアイテムを更新する場合、.Where(...)を使用すると、次の場合に結果が切り捨てられます。

mylist = mylist.Where(n => n.Id == ID).Select(n => { n.Property = ""; return n; }).ToList();

次のように、リスト内の特定のアイテムを更新できます。

mylist = mylist.Select(n => { if (n.Id == ID) { n.Property = ""; } return n; }).ToList();

変更しなくても、必ず返品してください。これにより、リストに保持されます。


1

新しいオブジェクトを作成せずに、インデックス値と最初と最後のインジケーターをリストに含めたいと思うことがよくあります。これにより、既存のクラスを変更せずに、リスト内のアイテムの位置や列挙などを確認し、リストの最初のアイテムにいるか最後のアイテムにいるかを知ることができます。

foreach (Item item in this.Items
    .Select((x, i) => {
    x.ListIndex = i;
    x.IsFirst = i == 0;
    x.IsLast = i == this.Items.Count-1;
    return x;
}))

以下を使用することで、任意のクラスを簡単に拡張できます。

public abstract class IteratorExtender {
    public int ListIndex { get; set; }
    public bool IsFirst { get; set; } 
    public bool IsLast { get; set; } 
}

public class Item : IteratorExtender {}

0

私がここで最善の解決策と考える答えが見つからなかったので、ここに私の方法:

「選択」を使用してデータを変更することは可能ですが、これはトリックです。とにかく、そのための「選択」はできません。データが必要になる前にLinqが実行されないため、 "ToList"と一緒に使用すると、変更が実行されます。とにかく、最善の解決策は「foreach」を使用することです。次のコードでは、次のことがわかります。

    class Person
    {
        public int Age;
    }

    class Program
    {
        private static void Main(string[] args)
        {
            var persons = new List<Person>(new[] {new Person {Age = 20}, new Person {Age = 22}});
            PrintPersons(persons);

            //this doesn't work:
            persons.Select(p =>
            {
                p.Age++;
                return p;
            });
            PrintPersons(persons);

            //with "ToList" it works
            persons.Select(p =>
            {
                p.Age++;
                return p;
            }).ToList();
            PrintPersons(persons);

            //This is the best solution
            persons.ForEach(p =>
            {
                p.Age++;
            });
            PrintPersons(persons);
            Console.ReadLine();
        }

        private static void PrintPersons(List<Person> persons)
        {
            Console.WriteLine("================");
            foreach (var person in persons)
            {
                Console.WriteLine("Age: {0}", person.Age);
            ;
            }
        }
    }

「foreach」の前に、linqを選択することもできます...


0
var item = (from something in someList
       select x).firstordefault();

を取得しitemitem.prop1=5;特定のプロパティを変更することができます。

または、dbからアイテムのリストを取得prop1し、返されたリスト内の各アイテムのプロパティを指定された値に変更しますか?もしそうなら、あなたはこれを行うことができます(私はそれをよりよく知っているので、私はVBでそれをしています):

dim list = from something in someList select x
for each item in list
    item.prop1=5
next

list変更とともに返されるすべてのアイテムが含まれます)


これは機能しますが、OPはfor eachループを記述せずにLINQステートメントを記述する方法を求めていたと思います。
fujiiface 2015年


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.