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

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

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

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

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

LINQ

LINQとはLanguage INtegrated Queryの略で、「統合言語クエリ」という意味です。C#やVisual Basicといった言語のコード内に記述することができるクエリです。

Q&A

解決済

3回答

1384閲覧

ジャグ配列で縦方向に比較

退会済みユーザー

退会済みユーザー

総合スコア0

C#

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

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

LINQ

LINQとはLanguage INtegrated Queryの略で、「統合言語クエリ」という意味です。C#やVisual Basicといった言語のコード内に記述することができるクエリです。

0グッド

1クリップ

投稿2019/02/13 17:52

前提・実現したいこと

ジャグ配列でのLINQの扱い方を勉強しています。
次のようなジャグ配列とindex=2が与えられていた時、

C#

1 int[][] cells = new int[][] 2 { 3 new int[]{ 0, 0, 0, 0}, //0 4 new int[]{ 0, 0, 0, 0}, //1 5 new int[]{ 1, 1, 1, 0}, //index=2 6 new int[]{ 0, 1, 1, 1}, //3 7 };

index=2と3の配列を縦方向に要素を比較して、1と1の要素で揃っているindex=2の配列のインデックスを全て格納した配列を
取得する処理をLINQで実装することはできますか?
この場合ですと、index=2の配列のインデックス1と2を格納した配列を取得するような処理です。
(やりたいことの説明が下手ですみません。試したことでLINQじゃない方法で処理を実装しています)。
イメージとしては下記です。

C#

1 int[][] cells = new int[][] 2 { 3 new int[]{ 0, 0, 0, 0}, //0 4 new int[]{ 0, 0, 0, 0}, //1 5 new int[]{ 1, 1, 1, 0}, //index=2 6             × 〇 〇 ×          ←縦に11が揃っている〇のインデックス、12を格納した配列を取得。 7 new int[]{ 0, 1, 1, 1}, //3 8 };

試したこと

LINQでの実装を考えましたが、わかりませんでした。
LINQで実装可能かどうかもわかりません。
LINQ以外の方法では、下記のコードで実装できました。

C#

1 int[][] cells = new int[][] 2 { 3 new int[]{ 0, 0, 0, 0}, //0 4 new int[]{ 0, 0, 0, 0}, //1 5 new int[]{ 1, 1, 1, 0}, //index=2 6 new int[]{ 0, 1, 1, 1}, //3 7 }; 8 9 List<int> indexlist = new List<int>(); 10 int index = 2; 11 for(int i=0; i<cells[index].Length; i++){ 12 if(cells[index][i]==1 && cells[index+1][i]==1){ 13 indexlist.Add(i); 14 } 15 } 16 int[] indexarr = indexlist.ToArray();

ご教示よろしくお願いします。

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

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

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

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

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

guest

回答3

0

前にも言ったかもしれませんが、やりにくいことを無理やり全部押し込めた LINQ クエリを作っても、読みにくくなるだけで何の意味もありません。
やりたいことを簡単に記述できるようクラスを作りましょう。

この場合は多次元配列やジャグ配列を LINQ で扱いたい、それも縦方向へのアクセスとインデクスが必要ということなので、拡張メソッドを作るのをお勧めします。
標準ライブラリの LINQ は基本的に縦方向へのアクセスは考えられていません。
また、インデクスが取れるのが Select だけなので、どうしてもそれを使おうと思えば余計なロジックが入ります。

C#

1using System; 2using System.Collections.Generic; 3using System.Linq; 4 5namespace ConsoleApp1 6{ 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 int[][] cells = new int[][] 12 { 13 new int[]{ 0, 0, 0, 0}, //0 14 new int[]{ 0, 0, 0, 0}, //1 15 new int[]{ 1, 1, 1, 0}, //index=2 16 new int[]{ 0, 1, 1, 1}, //3 17 }; 18 int index = 2; 19 var result = cells 20 .Skip(index) 21 .Take(2) 22 .ToColumns() 23 .IndiciesWhere(a => a.All(b => b == 1)) 24 .ToArray(); 25 foreach (var a in result) Console.WriteLine(a); 26 Console.ReadKey(); 27 } 28 } 29 30 public static class LinqExtension 31 { 32 public static IEnumerable<IEnumerable<T>> ToColumns<T>(this IEnumerable<IEnumerable<T>> source) 33 { 34 IReadOnlyList<IReadOnlyList<T>> sourceArray = source 35 .Select(a => a as IReadOnlyList<T> ?? a.ToArray()) 36 .ToArray(); 37 int columnLength = sourceArray 38 .Select(a => a.Count) 39 .Max(); 40 for (int i = 0; i < columnLength; i++) 41 { 42 yield return sourceArray 43 .Select(a => i < a.Count ? a[i] : default); 44 } 45 } 46 47 public static IEnumerable<int> IndiciesWhere<T>(this IEnumerable<T> source, Predicate<T> predicate) 48 { 49 int i = 0; 50 foreach (var item in source) 51 { 52 if (predicate(item)) yield return i; 53 i++; 54 } 55 } 56 } 57} 58

投稿2019/02/14 03:50

編集2019/02/14 04:07
Zuishin

総合スコア28660

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

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

papinianus

2019/02/14 05:21

.Select(a => i < a.Count ? a[i] : default(T))
Zuishin

2019/02/14 05:34

ありがとうございます。 default は C# 7.1 からでしたね。 default(T) に差し替えてください。 https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/statements-expressions-operators/default-value-expressions > C# 7.1 より、コンパイラが式の型を推定できる場合、default リテラルを既定の値式に使用することができます。 default リテラルは同等の default(T) (T は推定型) と同じ値を生成します。
papinianus

2019/02/14 05:42

それは存じませんでした。大変失礼しました。確かにいかにも推定できそうなパラメータですね。
退会済みユーザー

退会済みユーザー

2019/02/14 16:52

ご回答ありがとうございます。 ご提示いただいたコードの解読に時間をいただきます。 後ほどコメント致します。
Zuishin

2019/02/15 00:02 編集

ToColumns は縦方向に変換するメソッドです。 {{1, 1, 1, 0}, {0, 1, 1, 1}} が {{1, 0}, {1, 1}, {1, 1}, {0, 1}} になります。 IndiciesWhere は条件に合う要素のインデクスを返すメソッドです。 以下の二つのクエリは等価です。 {{1, 0}, {1, 1}, {1, 1}, {0, 1}}.IndiciesWhere(a => a.All(b => b == 1)) {{1, 0}, {1, 1}, {1, 1}, {0, 1}}.Select((row, index) => (row, index)).Where(a => a.row.All(b => b == 1)).Select(a => a.index) なお、以上は疑似コードなのでコンパイルできません。
退会済みユーザー

退会済みユーザー

2019/02/17 09:28

ご回答ありがとうございます。 ToColumnsメソッドや、IndiciesWhereメソッド、勉強になりました。 ありがとうございました。
guest

0

indexがもとの配列の1次元目のlength - 2におさまっているかなど、要求事項が厳しいので
全部を変換したほうがはやいと思いました。

csharp

1public static int[][] SelectIndexes(int[][] source) => source.Zip(source.Skip(1), (baseLine, nextLine) => baseLine.Zip(nextLine, (baseCol, nextCol) => baseCol == nextCol).Select((x,i) => x ? i : -1).Where(i => i > -1).ToArray()).ToArray();

どうしてもコメントさせていただきますが、Linqで考えるときに、indexをどうこうみたいな発想からは基本的に離れたほうがいいと思います。

投稿2019/02/14 01:03

papinianus

総合スコア12705

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

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

papinianus

2019/02/14 01:09

私は、このデータを何らかのフラグ値ではないかと思ってます。 ビットマスクっぽい動きなのかなーと想像しています。であれば最初からint(long)0b1110の1次元配列がいいし、ビット演算が典型でないフラグであるならEnumを使ったほうがいいのかなと思ってます。 あるいは2つセットで意味があるようなものならTupleとか。 ロジックが難しいときは、目的にあわないデータ構造を採用している可能性があると思います。
退会済みユーザー

退会済みユーザー

2019/02/14 17:24 編集

ご回答ありがとうございます。 ご提示いただいたコードの解読に時間をいただきます。 後ほどコメント致します。 質問の説明不足ですみません。ビットマスクがわからなかったので調べてみましたが、 ビットマスクのような用途ではないです。 フラグの意味が自分で理解できているかわかりませんが、フラグのつもりです。
退会済みユーザー

退会済みユーザー

2019/02/17 09:29

ご回答ありがとうございます。 Zipで変換する方法、勉強になりました。 ありがとうございました。
guest

0

ベストアンサー

こんにちは。

書いてあることそのままだとこういう感じでしょうか?

csharp

1var cells = new int[][] 2{ 3 new int[]{ 0, 0, 0, 0}, //0 4 new int[]{ 0, 0, 0, 0}, //1 5 new int[]{ 1, 1, 1, 0}, //index=2 6 new int[]{ 0, 1, 1, 1}, //3 7}; 8 9const int index = 2; 10 11var array = cells[index].Zip(cells[index + 1], (x, y) => (x, y)) // cells[index] と cells[index+1] を並べる 12 .Select((t, i) => (t.x, t.y, i)) // index を付与 13 .Where(t => t.x == 1 && t.y == 1) // 条件 14 .Select(t => t.i) // index を選択 15 .ToArray(); // => [ 1, 2 ]

目的がもっと具体的だったらもう少し意図に沿ったものが作れる気はします。


追記
.NET Framework 3.5以下でZipメソッドが使えないということが分かったので、以下のようにZipを自前で実装することはできます。

csharp

1internal static class MyLinqExtensions 2{ 3 public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> resultSelector) 4 { 5 using (var firstEnumerator = first.GetEnumerator()) 6 using (var secondEnumerator = second.GetEnumerator()) 7 while (firstEnumerator.MoveNext() && secondEnumerator.MoveNext()) 8 yield return resultSelector(firstEnumerator.Current, secondEnumerator.Current); 9 } 10}

投稿2019/02/14 00:16

編集2019/02/16 16:45
tamoto

総合スコア4110

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

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

退会済みユーザー

退会済みユーザー

2019/02/14 17:00

ご回答ありがとうございます。 LINQ初心者の自分には、1番理解しやすいLINQでした(コメントでご説明いただいているおかげもあります)。 1行目についての質問なのですが、 var array = cells[index].Zip(cells[index + 1], (x, y) => (x, y)) この(x, y) => (x, y)で返している(x, y)はタプルというものですか? ちなみにUnityでC#を扱っていて、C#4.0より上にする設定は都合上したくないので、 タプルの代替となるものはあったりしますか?
tamoto

2019/02/15 00:09

匿名型を使ってください。index付与の行も同様です。 (x, y) => new { x, y } (t, i) => new { t.x, t.y, i } それと、他の回答者さんもコメントしていますが、「Linqにindexの概念を持ち込むのは基本的にアンチパターン」であることを強く認識しておいてください。indexを使わなければならないようなデータ設計そのものが用途に合っていない可能性が高いです。
退会済みユーザー

退会済みユーザー

2019/02/15 16:10 編集

ご回答ありがとうございます。 アンチパターンであること認識しました。ご助言ありがとうございます。 ちなみに var array = cells[index].Zip(cells[index + 1], (x, y) => new {x, y}) .Select((t, i) => new{t.x, t.y, i}) // index を付与 .Where(t => t.x == 1 && t.y == 1) // 条件 .Select(t => t.i) // index を選択 .ToArray(); // => [ 1, 2 ]; としたところ、 Type `int[]' does not contain a definition for `Zip' and no extension method `Zip' of type `int[]' could be found. Are you missing an assembly reference? というエラーが出てしまいました。 cells[index].Zipは使えないですか?
tamoto

2019/02/16 16:37

まさかと思って調べましたが、Zipは.NET Framework 4.0で追加されたメソッドでしたね……NET3.5だとそのままでは使えないということになります。 当然自前で実装してしまえば使えますが……とりあえず回答に実装を追記しておきます。
退会済みユーザー

退会済みユーザー

2019/02/17 09:29

ご回答ありがとうございます。 NET3.5対応コードのご教示ありがとうございます。 とても勉強になりました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問