リストアイテムを最良の方法で置き換える方法


97
if (listofelements.Contains(valueFieldValue.ToString()))
{
    listofelements[listofelements.IndexOf(valueFieldValue.ToString())] = value.ToString();
}

上記のように交換しました。これよりも他に配置する最善の方法はありますか?

回答:


108

Lambdaを使用してリスト内のインデックスを検索し、このインデックスを使用してリストアイテムを置き換えます。

List<string> listOfStrings = new List<string> {"abc", "123", "ghi"};
listOfStrings[listOfStrings.FindIndex(ind=>ind.Equals("123"))] =  "def";

14
-1を確認してください。アイテムがコレクションに存在しない場合
Surender Singh Malik 2015

3
さらに、FindIndexを使用するための1つ
アーロンバーカー

2
これは、オブジェクトの比較にも使用できるため、IMHOの最良の普遍的な答えです。
Simcha Khabinsky 2017

-1をチェックするFejの拡張機能を参照してください。単純なEqualsテストでは、古き良きIndexOfものも同様に機能し、ティムの回答のように、より簡潔です。
ToolmakerSteve

109

あなたはそれをより読みやすく、より効率的にすることができます:

string oldValue = valueFieldValue.ToString();
string newValue = value.ToString();
int index = listofelements.IndexOf(oldValue);
if(index != -1)
    listofelements[index] = newValue;

これは、インデックスを1回だけ要求します。あなたのアプローチは、Contains最初にすべてのアイテムをループする必要がある(最悪の場合)を使用し、次にIndexOfアイテムを再度列挙する必要があるを使用しています。


2
これは、リテラル-int、stringを見つけるための正しい答えですが、オブジェクトを見つけるにはあまり適していません。でも、ロックちゃんの答えは普遍的なのでいいです。
Simcha Khabinsky 2017

1
@SimchaKhabinsky:参照型でも機能します。型はオーバーライドする必要がありますEquals。そうしないと、同じ参照の場合にのみオブジェクトが見つかります。これstringはオブジェクトでもあることに注意してください(参照タイプ)。
Tim Schmelter 2017

はい、そうです。しかし、私は多くの開発者が実装を忘れてしまったことを見Equals てきました。また、時には同時に実装しなければならないことも覚えておく必要がありますGetHashCode
Simcha Khabinsky 2017

1
@SimchaKhabinsky:はい、オーバーライドするGetHashCode場合は常にオーバーライドする必要がありますEqualsGetHashCode、オブジェクトがセット(fe DictionaryまたはHashSet)に格納されているIndexOf場合Containsにのみ使用されるため、またはでのみ使用されEqualsます。
Tim Schmelter 2017

ティム、これ対ロックンちゃんについて質問がある。をIndexOf使用するドキュメントを読みましたEqualityComparer<T>.Default。それは最終的にitem.Equals(target)リストの各アイテムを要求するので、ロックちゃんの答えとまったく同じ動作をするということですか?
ToolmakerSteve

16

1つの要素を置き換えるためにリストに2回アクセスしています。単純なforループで十分だと思います:

var key = valueFieldValue.ToString();
for (int i = 0; i < listofelements.Count; i++)
{
    if (listofelements[i] == key)
    {
        listofelements[i] = value.ToString();
        break;
    }
}

1
@gzaxx。「1つの要素を置き換えるためにリストに2回アクセスしています。単純なforループで十分だと思います。」そして、forループメイトのリストに何回アクセスしますか?
Pap

5
@Pap申し訳ありませんが、私は十分に明確にされていませんでした。彼は自分のリストを2回繰り返しています(最初に項目がリスト内にあるかどうかを確認するため、次に項目インデックスを取得するため)。
gzaxx 2014

13

拡張メソッドを使用しないのはなぜですか?

次のコードを検討してください。

        var intArray = new int[] { 0, 1, 1, 2, 3, 4 };
        // Replaces the first occurance and returns the index
        var index = intArray.Replace(1, 0);
        // {0, 0, 1, 2, 3, 4}; index=1

        var stringList = new List<string> { "a", "a", "c", "d"};
        stringList.ReplaceAll("a", "b");
        // {"b", "b", "c", "d"};

        var intEnum = intArray.Select(x => x);
        intEnum = intEnum.Replace(0, 1);
        // {0, 0, 1, 2, 3, 4} => {1, 1, 1, 2, 3, 4}
  • 重複するコードはありません
  • 長いlinq式を入力する必要はありません
  • 追加の使用の必要はありません

ソースコード:

namespace System.Collections.Generic
{
    public static class Extensions
    {
        public static int Replace<T>(this IList<T> source, T oldValue, T newValue)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            var index = source.IndexOf(oldValue);
            if (index != -1)
                source[index] = newValue;
            return index;
        }

        public static void ReplaceAll<T>(this IList<T> source, T oldValue, T newValue)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            int index = -1;
            do
            {
                index = source.IndexOf(oldValue);
                if (index != -1)
                    source[index] = newValue;
            } while (index != -1);
        }


        public static IEnumerable<T> Replace<T>(this IEnumerable<T> source, T oldValue, T newValue)
        {
            if (source == null)
                throw new ArgumentNullException("source");

            return source.Select(x => EqualityComparer<T>.Default.Equals(x, oldValue) ? newValue : x);
        }
    }
}

最初の2つのメソッドが追加され、参照タイプのオブジェクトを適切に変更します。もちろん、すべてのタイプに対して3番目の方法のみを使用できます。

PS マイクの観察のおかげで、ReplaceAllメソッドを追加しました。


1
「場所で参照型のオブジェクトを変更する」 -かどうかをT参照型であるかどうかは無関係です。重要なのは、リストを変更(変更)するか、新しいリストを返すかです。あなたはので、もちろん第3の方法は、元のリストを変更しないであろうことはできません だけで第三の方法を使用して...。最初の方法は、尋ねられた特定の質問に答える方法です。優れたコード-メソッドの機能に関する説明を修正するだけです:)
ToolmakerSteve '11年

7

rokkuchanの答えに続いて、ほんの少しのアップグレード:

List<string> listOfStrings = new List<string> {"abc", "123", "ghi"};

int index = listOfStrings.FindIndex(ind => ind.Equals("123"));
if (index > -1)
    listOfStrings[index] =  "def";

5

FindIndex値とラムダを使用して、値を検索および置換します。

int j = listofelements.FindIndex(i => i.Contains(valueFieldValue.ToString())); //Finds the item index

lstString[j] = lstString[j].Replace(valueFieldValue.ToString(), value.ToString()); //Replaces the item by new value

3

述語条件に基づく次の拡張を使用できます。

    /// <summary>
    /// Find an index of a first element that satisfies <paramref name="match"/>
    /// </summary>
    /// <typeparam name="T">Type of elements in the source collection</typeparam>
    /// <param name="this">This</param>
    /// <param name="match">Match predicate</param>
    /// <returns>Zero based index of an element. -1 if there is not such matches</returns>
    public static int IndexOf<T>(this IList<T> @this, Predicate<T> match)
    {
        @this.ThrowIfArgumentIsNull();
        match.ThrowIfArgumentIsNull();

        for (int i = 0; i < @this.Count; ++i)
            if (match(@this[i]))
                return i;

        return -1;
    }

    /// <summary>
    /// Replace the first occurance of an oldValue which satisfies the <paramref name="removeByCondition"/> by a newValue
    /// </summary>
    /// <typeparam name="T">Type of elements of a target list</typeparam>
    /// <param name="this">Source collection</param>
    /// <param name="removeByCondition">A condition which decides is a value should be replaced or not</param>
    /// <param name="newValue">A new value instead of replaced</param>
    /// <returns>This</returns>
    public static IList<T> Replace<T>(this IList<T> @this, Predicate<T> replaceByCondition, T newValue)
    {
        @this.ThrowIfArgumentIsNull();
        removeByCondition.ThrowIfArgumentIsNull();

        int index = @this.IndexOf(replaceByCondition);
        if (index != -1)
            @this[index] = newValue;

        return @this;
    }

    /// <summary>
    /// Replace all occurance of values which satisfy the <paramref name="removeByCondition"/> by a newValue
    /// </summary>
    /// <typeparam name="T">Type of elements of a target list</typeparam>
    /// <param name="this">Source collection</param>
    /// <param name="removeByCondition">A condition which decides is a value should be replaced or not</param>
    /// <param name="newValue">A new value instead of replaced</param>
    /// <returns>This</returns>
    public static IList<T> ReplaceAll<T>(this IList<T> @this, Predicate<T> replaceByCondition, T newValue)
    {
        @this.ThrowIfArgumentIsNull();
        removeByCondition.ThrowIfArgumentIsNull();

        for (int i = 0; i < @this.Count; ++i)
            if (replaceByCondition(@this[i]))
                @this[i] = newValue;

        return @this;
    }

注:-ThrowIfArgumentIsNull拡張の代わりに、次のような一般的なアプローチを使用できます。

if (argName == null) throw new ArgumentNullException(nameof(argName));

したがって、これらの拡張機能を使用したケースは、次のように解決できます。

string targetString = valueFieldValue.ToString();
listofelements.Replace(x => x.Equals(targetString), value.ToString());

1

最高かどうかはわかりませんが、あなたもそれを使うことができます

List<string> data = new List<string>
(new string[]   { "Computer", "A", "B", "Computer", "B", "A" });
int[] indexes = Enumerable.Range(0, data.Count).Where
                 (i => data[i] == "Computer").ToArray();
Array.ForEach(indexes, i => data[i] = "Calculator");

1

または、Rusian L.の提案に基づいて、検索しているアイテムがリストに複数回存在する可能性がある場合:

[Extension()]
public void ReplaceAll<T>(List<T> input, T search, T replace)
{
    int i = 0;
    do {
        i = input.FindIndex(i, s => EqualityComparer<T>.Default.Equals(s, search));

        if (i > -1) {
            FileSystem.input(i) = replace;
            continue;
        }

        break;  
    } while (true);
}

1

このようなラムダ式を使用できます。

int index = listOfElements.FindIndex(item => item.Id == id);  
if (index != -1) 
{
    listOfElements[index] = newValue;
}

0

私はそれをすばやく簡単に行うのに最適です

  1. リストからアイテムを探します

    var d = Details.Where(x => x.ProductID == selectedProduct.ID).SingleOrDefault();
  2. 現在からクローンを作成

    OrderDetail dd = d;
  3. クローンを更新

    dd.Quantity++;
  4. リストからインデックスを見つける

    int idx = Details.IndexOf(d);
  5. (1)で作成されたアイテムを削除

      Details.Remove(d);
  6. インサート

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