質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

5回答

2350閲覧

CSVファイルのフラグ漏れチェック処理方法について

cinnamon

総合スコア7

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2016/12/27 22:33

編集2016/12/28 00:28

###前提・実現したいこと

C#で次のようなCSVファイルを読み込みます。

Number,Flag
1000,0
1000,1
1001,0
1001,1
1002,0
1002,0
1002,1
1003,0
1003,1
1003,1
1004,0
1005,0
1005,1
・・・

次の条件であるかどうか、のチェックをする必要があります。
・Numberが同じもので、0と1が両方ふくまれているかどうか

例ですと、Numberが1000のものでFlagが0と1の行があるのでOK、
Numberが1004はFlagが0の行しかないのでNG、
Number1002のように、Flag0が複数行ある場合もありますが、
その場合でもFlagに0と1があればOKです。
(上記の例でいけば1004だけがNGです)
Flagには0と1以外のデータは入ってきません。

CSVはCSVHelper(https://joshclose.github.io/CsvHelper/)を使って
読み込もうと考えています。
.NET Frameworkは最新のものが使える環境です。

上記のチェックを行いたいのですが、どのような処理で行っていくのがよいかが
思いつかず、よい方法がありましたらご教授願います。

(追記)
NumberはCSVの状態では必ず昇順・降順に並んでいるという保証は無く、
一部順番がばらばらであったりする場合もあります。
データベースは使わずプログラム内のみで完結させたいと考えています。

どのような処理方法を採用すればよいのか、と考え方に悩んでいます。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

katsuya141

2016/12/28 00:09

DBを使いますか、配列のみで処理しますか?
katsuya141

2016/12/28 00:11

Noは昇順または降順に並んでいますかランダムですか
ardin

2016/12/28 00:12

悩んでいるプログラミング部分がありますか?単純に考え方に悩んでいますか?
cinnamon

2016/12/28 00:29

ありがとうございます。追記しましたが、考え方に悩んでおります。
tamoto

2016/12/28 00:36

この質問からは「条件をチェックしたい」のはわかっても、チェックした結果「どうしたいのか」がよくわかりません。例えば「条件を満たさないものがあったらエラーで停止させる」「条件を満たさないものを全部列挙する」「条件を満たさないものは無視して、満たすものだけを配列にする」など、いろんなやり方が考えられます。
cinnamon

2016/12/28 01:29

失礼しました。チェックした結果どうしたいかの記載が不足していました。チェックした結果、条件を満たさないものをすべて列挙して最終的にはログとして出力します。本来0と1が両方含まれているはずが、なんらかの原因で抜けていないか、のチェックを行い、抜けがあったNumberについて確認が必要になります。そのため、条件を満たさないものはすべて列挙する必要があります。
cinnamon

2016/12/28 02:32

ご回答いただいた皆様、ありがとうございます。いろいろな方法をご提示いただき、試してみたいと思います。試してみた結果も改めてご報告したいと思います。
cinnamon

2016/12/28 06:34

皆様ありがとうございました。意図したとおりのデータがチェックできました。今回ご提示いただいた方法でいろいろな方法があり勉強になりました。Dictionaryを使った方法は処理が早く、私の環境でLINQの1/7の処理速度でした。LINQの方は処理が簡潔でわかりやすいことと、処理速度が遅いとはいえ体感できるレベルのものでは無かったので、LINQを使った方法を利用したいと思います。
guest

回答5

0

主要部をメソッドチェーンで書いてみました。

var answer = fileText // ファイルの中身 string .Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries) // 改行文字"\r\n"で行に分離 .Skip(1) // ヘッダ("Number,Flag ")を捨てる .Select(l => l.Trim()) // 行両端のスペースを除く .Select(l => // 行を Number と Flag 分離 { var fields = l.Split(new[] { ',' }); return new { Number = fields.First(), Flag = fields.Last() }; }) .GroupBy(l => l.Number) // Number でグルーピング .OrderBy(g => g.Key) // Number で並べ替え .Select(g => new { Number = g.Key, OK = g.Max(l => l.Flag) != g.Min(l => l.Flag) }) // Number + bool にする .Select(a => $"Number:{a.Number}\tOK?:{a.OK}") // 表現形(string)へ変更 .Aggregate((a, b) => a + "\r\n" + b); // 表現形を統合

投稿2016/12/28 02:45

hihijiji

総合スコア4150

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cinnamon

2016/12/28 06:29

ありがとうございます。LINQは簡潔に記載ができるのでよいですね。
guest

0

Csvだし基本的には逐次処理。
ただし複数行の判定が必要のため、一旦全て読み取ってから結果の判定をしましょう。
Dictionaryを使っても良いし、それが気持ち悪いなら最後に整形してもいいです。

C#

1class Flags 2{ 3 // フラグは別にenumのフラグ合成でもなんでもいいけど…数字のゼロがフラグ合成しにくいから 4 public bool Zero { get; set; } 5 public bool One { get; set; } 6 public bool IsValid { get { return Zero && One; } } 7 public void SetFlag(int flag) 8 { 9 if (flag == 0) Zero = true; 10 else if (flag == 1) One = true; 11 } 12} 13 14class Csv 15{ 16 public int Number { get; set; } 17 public int Flag { get; set; } 18} 19 20IDictionary<int, Flags> Import() 21{ 22 // ... 後を考えればキーでソート済みにしたい 23 var dictionary = new SortedDictionary<int, Flags>(); 24 25 foreach (Csv csv ... /* CsvHelperから取得 */) 26 { 27 Flags flags; 28 if (!dictionary.TryGetValue(csv.Number, out flags)) 29 { 30 flags = new Flags(); 31 dictionary.Add(csv.Number, flags); 32 } 33 flags.SetFlag(csv.Flag); 34 } 35 36 return dictionary; 37} 38 39void Check(IDictionary<int, Flags> results) 40{ 41 foreach (var result in results) 42 { 43 Console.WriteLine($"Number: {result.Key} IsValid: {result.Value.IsValid}"); 44 } 45}

投稿2016/12/28 01:49

haru666

総合スコア1591

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cinnamon

2016/12/28 02:31

ありがとうございます。dictionaryでの方法も試してみます。
guest

0

ベストアンサー

補足がついたので回答してみます。
「条件を満たしていない (== Flagが片方しか存在しない) Numberを全てリストアップ」をそのままコードにしてみました。

csharp

1var result = source // sourceは new { Number = 1001, Flag = 0 } みたいなのを想定 2 .Distinct() // NumberとFlagが同じものを除去して、 3 .GroupBy(x => x.Number) // Numberが同じものでグルーピングして、 4 .Where(x => x.Count() == 1) // Countが1 (== Flagが片方しか無い) のものでフィルタして、 5 .Select(x => x.Key); // Key (== Number) を選択 6 7/*/ 8 9// 質問のコードにあるデータの場合は 10result = [ 1004 ]; 11 12/*/

投稿2016/12/28 01:48

tamoto

総合スコア4110

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cinnamon

2016/12/28 02:29

ありがとうございます。こちらの方法でも試してみます。
cinnamon

2016/12/28 06:36

意図したとおりの処理ができ、コードも簡潔になりました。ありがとうございました。
guest

0

例えば、

  1. 完全に重複した要素を排除する。
  2. Flag = 0/1でNumberをグルーピングする。
  3. Flag0グループとFlag1グループの積集合をとる。
  4. その結果がOKなNumberの集合。

やっつけで作った:

C#

1using System; 2using System.Linq; 3 4public class Test 5{ 6 public static void Main() 7 { 8 var source = new[]{ 9 new Element{Number = 1000,Flag = 0}, 10 new Element{Number = 1000,Flag = 1}, 11 new Element{Number = 1001,Flag = 0}, 12 new Element{Number = 1001,Flag = 1}, 13 new Element{Number = 1002,Flag = 0}, 14 new Element{Number = 1002,Flag = 0}, 15 new Element{Number = 1002,Flag = 1}, 16 new Element{Number = 1003,Flag = 0}, 17 new Element{Number = 1003,Flag = 1}, 18 new Element{Number = 1003,Flag = 1}, 19 new Element{Number = 1004,Flag = 0}, 20 new Element{Number = 1005,Flag = 0}, 21 new Element{Number = 1005,Flag = 1}, 22 new Element{Number = 1006,Flag = 0}, 23 }; 24 25 var group = source.Distinct().GroupBy(e => e.Flag, e => e.Number); 26 var ones = group.Where(g => g.Key == 1).SelectMany(x => x); 27 var zeros = group.Where(g => g.Key == 0).SelectMany(x => x); 28 var whole = group.SelectMany(x => x); 29 30 var ok = ones.Intersect(zeros); 31 var ng = whole.Except(ok); 32 33 34 Console.WriteLine("==========ok==========="); 35 foreach (var x in ok) 36 Console.WriteLine(x); 37 38 Console.WriteLine("==========ng==========="); 39 40 foreach (var x in ng) 41 Console.WriteLine(x); 42 43 } 44} 45 46public struct Element 47{ 48 public int Number; 49 public int Flag; 50}

例えば、
0. Numberでグルーピングする。
0. 各グループが0と1の両方を含むかいい感じにフィルタ
0. 結果のKey(=Number)を抽出

投稿2016/12/28 01:09

編集2016/12/28 01:35
ozwk

総合スコア13528

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cinnamon

2016/12/28 01:31

ありがとうございます。LINQを使った方法ですね。一度試してみます。
guest

0

マップ(Dictionary)を利用するのが一般的です。

以下疑似コードです。

C#

1// マップ(Dictionary)を利用 2// キー = Number, 値 = Flagの出現状態。 0 or 1 or 2=0と1の両方を含む 3 4// CSV行の走査 5 // 行から入力のNumberとFlagを取得 6 7 // マップから入力Numberをキーとして要素を検索 8 // 存在しない 9 // マップにキー=入力Number, 値=入力Flag値として要素を追加 10 // 存在する 11 // マップの値=2 12 // すでに両方含まれるので何もしない。 13 // マップの値=0 or 1 14 // 入力のFlag値と同じ 15 // 何もしない。 16 // 入力のFlag値と異なる 17 // 要素のFlag値に2をセットしてマップに再登録 18 19// マップの走査 20 // マップのFlag値=2 →OK 21 // それ以外 →NG

投稿2016/12/28 01:07

can110

総合スコア38266

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cinnamon

2016/12/28 01:30

ありがとうございます。考え方もわかりやすくすっきりできそうです。一度試したいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問