質問
テキスト ファイル (CSV 形式) を 1 行ずつ読み取り、データを HashTable に配置する基本的な C# コンソール アプリケーションがあります。行の最初の CSV 項目はキー (id num) で、行の残りの部分は値です。しかし、インポート ファイルに、あるはずのない重複キーがいくつか含まれていることがわかりました。ファイルをインポートしようとすると、HashTable に重複したキーを含めることができないため、アプリケーションがエラーになります。ただし、プログラムでこのエラーを処理できるようにしたいと考えています。重複キーが見つかった場合は、そのキーを配列リストに入れて、残りのデータをハッシュテーブルにインポートし続けたいと思います。C# でこれを行うにはどうすればよいですか
これが私のコードです:
プライベート静的ハッシュテーブルインポートファイル(ハッシュテーブルMyHashtable、String MyFileName){
StreamReader sr = new StreamReader(myFileName);
CSVReader csvReader = new CSVReader();
ArrayList tempArray = new ArrayList();
int count = 0;
while (!sr.EndOfStream)
{
String temp = sr.ReadLine();
if (temp.StartsWith(" "))
{
ServMissing.Add(temp);
}
else
{
tempArray = csvReader.CSVParser(temp);
Boolean first = true;
String key = "";
String value = "";
foreach (String x in tempArray)
{
if (first)
{
key = x;
first = false;
}
else
{
value += x + ",";
}
}
myHashtable.Add(key, value);
}
count++;
}
Console.WriteLine("Import Count: " + count);
return myHashtable;
}
解決
if (myHashtable.ContainsKey(key))
duplicates.Add(key);
else
myHashtable.Add(key, value);
他のヒント
より良い解決策は、キーをハッシュ テーブルに追加する前に、ContainsKey を呼び出してキーが存在するかどうかを確認することです。この種のエラーで例外をスローすると、パフォーマンスが低下し、プログラム フローは改善されません。
ContainsKey にはすべての項目に対して一定の O(1) オーバーヘッドがありますが、例外をキャッチすると重複する項目だけでパフォーマンス ヒットが発生します。
ほとんどの状況では、キーをチェックすると思いますが、この場合は例外をキャッチした方が良いでしょう。
すべての挿入に対するオーバーヘッドを小さくして、セカンダリ リストでの複数のヒットを回避するソリューションを次に示します。
Dictionary<T, List<K>> dict = new Dictionary<T, List<K>>();
//Insert item
if (!dict.ContainsKey(key))
dict[key] = new List<string>();
dict[key].Add(value);
これを非表示にする型で辞書をラップしたり、辞書のメソッドまたは拡張メソッドに含めたりすることができます。
(たとえば) 4 つを超える CSV 値がある場合は、 価値 文字列の連結は遅い関数であるため、変数でも StringBuilder を使用する必要があります。
うーん、170万行?そのような負荷にこれを提供することは躊躇します。
LINQ を使用してこれを行う 1 つの方法を次に示します。
CSVReader csvReader = new CSVReader();
List<string> source = new List<string>();
using(StreamReader sr = new StreamReader(myFileName))
{
while (!sr.EndOfStream)
{
source.Add(sr.ReadLine());
}
}
List<string> ServMissing =
source
.Where(s => s.StartsWith(" ")
.ToList();
//--------------------------------------------------
List<IGrouping<string, string>> groupedSource =
(
from s in source
where !s.StartsWith(" ")
let parsed = csvReader.CSVParser(s)
where parsed.Any()
let first = parsed.First()
let rest = String.Join( "," , parsed.Skip(1).ToArray())
select new {first, rest}
)
.GroupBy(x => x.first, x => x.rest) //GroupBy(keySelector, elementSelector)
.ToList()
//--------------------------------------------------
List<string> myExtras = new List<string>();
foreach(IGrouping<string, string> g in groupedSource)
{
myHashTable.Add(g.Key, g.First());
if (g.Skip(1).Any())
{
myExtras.Add(g.Key);
}
}
皆さん、ありがとうございました。結局、ContainsKey() メソッドを使用しました。おそらく 30 秒ほど長くかかりますが、私の目的には問題ありません。約 170 万行をロードしていますが、プログラムが 2 つのファイルをロードして比較し、いくつかのファイルを書き出すのに合計約 7 分かかります。ファイルの比較と書き込みには約 2 秒しかかかりません。