NeededList
という名前のリストがあります。このリストの各アイテムをチェックして、データベースに存在するかどうかを確認する必要があります。データベースに存在する場合は、リストから削除する必要があります。しかし、繰り返し処理している間はリストを変更できません。どうすればこれを機能させることができますか?
これまでの私のコードは次のとおりです。
For Each Needed In NeededList
Dim Ticker = Needed.Split("-")(0).Trim()
Dim Year = Needed.Split("-")(1).Trim()
Dim Period = Needed.Split("-")(2).Trim()
Dim Table = Needed.Split("-")(3).Trim()
Dim dr As OleDbDataReader
Dim cmd2 As New OleDb.OleDbCommand("SELECT * FROM " & Table & " WHERE Ticker = ? AND [Year] = ? AND Period = ?", con)
cmd2.Parameters.AddWithValue("?", Ticker)
cmd2.Parameters.AddWithValue("?", Year)
cmd2.Parameters.AddWithValue("?", Period)
dr = cmd2.ExecuteReader
If dr.HasRows Then
NeededList.Remove(Needed)
End If
Next
いいえ、for eachを使用してそれを行うことはできませんが、昔ながらのfor ..ループを使用してそれを行うことができます。
コツは、最後から始めて後方にループすることです。
For x = NeededList.Count - 1 to 0 Step -1
' Get the element to evaluate....
Dim Needed = NeededList(x)
.....
If dr.HasRows Then
NeededList.RemoveAt(x)
End If
Next
現在の要素が削除されているため要素をスキップするリスクがないため、この方法でループにアプローチする必要があります。
たとえば、コレクションの4番目の要素を削除するとします。その後、5番目の要素が4番目になります。ただし、インデクサーは5まで上がります。このようにして、前の5番目の要素(現在は4番目の位置にある)は評価されません。もちろん、インデクサーの値を変更しようとすることもできますが、これは常に悪いコードと発生するのを待っているバグで終わります。
安全のために、ToList()
を使用してコピーを作成します。
For Each Needed In NeededList.ToList()
Dim Ticker = Needed.Split("-")(0).Trim()
...
If dr.HasRows Then
NeededList.Remove(Needed)
End If
Next
ステップ-1を使用して、すべてのインデックスを反復するForループを使用できます。
For i as Integer = NeededList.Count - 1 to 0 Step -1
Dim Needed = NeededList(i)
'this is a copy of your code
Dim Ticker = Needed.Split("-")(0).Trim()
Dim Year = Needed.Split("-")(1).Trim()
Dim Period = Needed.Split("-")(2).Trim()
Dim Table = Needed.Split("-")(3).Trim()
Dim dr As OleDbDataReader
Dim cmd2 As New OleDb.OleDbCommand("SELECT * FROM " & Table & " WHERE Ticker = ? AND [Year] = ? AND Period = ?", con)
cmd2.Parameters.AddWithValue("?", Ticker)
cmd2.Parameters.AddWithValue("?", Year)
cmd2.Parameters.AddWithValue("?", Period)
dr = cmd2.ExecuteReader
'MODIFIED CODE
If dr.HasRows Then NeededList.RemoveAt(i)
Next i
配列の内容(またはFor Each
で高速に列挙できるものはFor Each
ループでは変更できません。単純なFor
ループを使用して、すべてのインデックスを反復処理する必要があります。
ヒント:インデックスを削除するので、最後のインデックスから始めて、最初のインデックスに向かって作業を進めることをお勧めします。これにより、インデックスを削除するたびにスキップされないようにします。
リストは最後から拡大および縮小するため、リストをreverseの順序で反復することで問題を解決できます。
コレクションを元に戻すことは、コピーを返すよりも速くなります。したがって、速度が必要な場合は、コレクションを操作する前にlist.Reverse()を使用してください。
ReverseToList: 00:00:00.0005484
CopyWithToList: 00:00:00.0017638
CopyWithForeach: 00:00:00.0141009
For Each Needed In NeededList.Reverse()
Dim Ticker = Needed.Split("-")(0).Trim()
'...
If dr.HasRows Then
NeededList.Remove(Needed)
End If
Next
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
public class Program
{
public static void Main()
{
List<int> items = new List<int>();
items = Enumerable.Range(0, 1000000).ToList();
Reverse(items);
CopyWithToList(items);
CopyWithForeach(items);
}
public static void Reverse<T>(List<T> list)
{
var sw = Stopwatch.StartNew();
list.Reverse();
sw.Stop();
Console.WriteLine("ReversedList: {0}", sw.Elapsed);
}
public static void CopyWithToList<T>(List<T> list)
{
var sw = Stopwatch.StartNew();
List<T> copy = list.ToList();
sw.Stop();
Console.WriteLine("CopyWithToList: {0}", sw.Elapsed);
}
public static void CopyWithForeach<T>(List<T> list)
{
var sw = Stopwatch.StartNew();
List<T> copy = new List<T>();
foreach (T item in list) {
copy.Add(item);
}
sw.Stop();
Console.WriteLine("CopyWithForeach: {0}", sw.Elapsed);
}
}
リストの要素の順序を逆にしても、For Each
およびReverse
拡張機能を使用してIEnumerable Cast
を使用できます。
List(Of String)を使用した簡単な例:
For Each Needed In NeededList.Cast(Of List(Of String)).Reverse()
If dr.HasRows Then
NeededList.Remove(Needed)
End If
Next
これはどうですか(イテレーションは必要ありません):
NeededList = (NeededList.Where(Function(Needed) IsNeeded(Needed)).ToList
Function IsNeeded(Needed As ...) As Boolean
...
Return Not dr.HasRows
End Function
いいえ、作業中のリストから削除することはできません。各Strの文字列としてlistOfStringsの文字列ListOfStrings If Str.Equals( "Pat")Then Dim index = listOfStrings.IndexOf(Str)listOfStrings .RemoveAt(index)End If Next
しかし、この方法はあなたのリストのコピーを作成し、そこから削除します。各Str For As String In listOfStrings If Str.Equals( "Pat")Then Dim index = listOfStringsCopy.IndexOf(Str)listOfStringsCopy.RemoveAt(index)End If Next