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

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

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

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

Q&A

解決済

3回答

1936閲覧

指定した文字以外の文字が最初に出現するインデックス

退会済みユーザー

退会済みユーザー

総合スコア0

C#

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

0グッド

1クリップ

投稿2021/12/05 14:19

編集2021/12/05 14:23

前提・実現したいこと

任意の文字列に対して、指定した文字以外の文字が最初に出現するインデックスを取得するには、
どのような実装をすればよいでしょうか。
任意の文字列に対して、指定した文字が最初に出現するインデックスを取得するには、
IndexOfメソッドを使えばよいことは知っていますが、
「指定した文字」ではなく、「指定した文字以外の文字」を検索する方法についてです。

試したことに詳細は記載しましたが、1つは、FindIndexメソッドを使うことによって、実装はできました。
ただ、こちらの記事にあるように、
今回試したコードはLINQではありませんが、LINQみたいなコードだと少し遅いと思うので
(気にするほどではないかもしれませんが)、
できれば、提示した記事のStringクラスを使ったやり方のように
(提示記事の内容は今回の質問の本題とは異なりますが)、
なにかスマートな方法があればと思い、質問させていただきました。
なお、ループを使った実装は考えていません(それならば、FindIndexメソッドを使う方法にします)。

質問1
冒頭の質問です。

質問2
FindIndexメソッドについて調べたら、これらは配列やリストで用意されているメソッドだとわかりました。
同等のメソッドは、LINQに存在しますか?あれば教えていただきたいです。
ToCharArrayメソッドを呼び出す手間をなくしたいという理由もあります。

試したこと

C#

1 // 任意の文字列 2 string s = "aaaabc"; 3 char[] charArray = s.ToCharArray(); 4 // 指定文字 5 char letter = 'a'; 6 int index = Array.FindIndex(charArray, c => c != letter); 7 Debug.WriteLine(index); // 4

補足情報(FW/ツールのバージョンなど)

.NET 5.0

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

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

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

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

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

guest

回答3

0

今回試したコードはLINQではありませんが、LINQみたいなコードだと少し遅いと思うので
(気にするほどではないかもしれませんが)

もう解決されたようですが、もし速度を気にするのであれば、Span<T>を使うといいかもしれません。
メモリコピーが発生しないので高速に動作します。(LINQとは相性良くないけど)
拡張メソッドで実装したFindIndexの比較結果を載せておきます。

cs

1using BenchmarkDotNet.Attributes; 2using BenchmarkDotNet.Diagnosers; 3using BenchmarkDotNet.Running; 4using System; 5using System.Linq; 6using System.Collections.Generic; 7 8public static class StringExtention 9{ 10 public static int FindIndex<T>(this IEnumerable<T> source, Predicate<T> match) 11 { 12 int index = 0; 13 foreach (T item in source) 14 { 15 if (match(item)) 16 { 17 return index; 18 } 19 index++; 20 } 21 return -1; 22 } 23 24 public static int FindIndex<T>(this ReadOnlySpan<T> span, Predicate<T> match) 25 { 26 for (int i = 0; i < span.Length; i++) 27 { 28 if (match(span[i])) 29 { 30 return i; 31 } 32 } 33 return -1; 34 } 35 36 public static int FindIndex(this string str, Predicate<char> match) => FindIndex(str.AsSpan(), match); 37} 38 39//計測用テストクラス 40[MemoryDiagnoser(false)] 41[ShortRunJob] 42public class Test 43{ 44 private const string _String = "aaaabc"; 45 private const char _SearchChar = 'a'; 46 private static Predicate<char> _Predicate = (c) => c != _SearchChar; 47 48 [Benchmark] 49 public int ArrayFindIndex() => Array.FindIndex(_String.ToArray(), _Predicate); 50 51 [Benchmark] 52 public int ListFindIndex() => _String.ToList().FindIndex(_Predicate); 53 54 [Benchmark] 55 public int EnumerableFindIndex() => _String.AsEnumerable().FindIndex(_Predicate); 56 57 [Benchmark] 58 public int SpanFindIndex() => _String.FindIndex(_Predicate); 59} 60 61class Program 62{ 63 static void Main() 64 { 65 BenchmarkRunner.Run<Test>(); 66 } 67}

【計測結果】

MethodMeanErrorStdDevAllocated
ArrayFindIndex145.08 ns131.657 ns7.217 ns144 B
ListFindIndex108.14 ns13.836 ns0.758 ns136 B
EnumerableFindIndex43.90 ns0.860 ns0.047 ns32 B
SpanFindIndex13.40 ns2.107 ns0.116 ns-

投稿2021/12/06 07:57

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

退会済みユーザー

退会済みユーザー

2021/12/06 08:21

ご回答ありがとうございます。 計測結果のご提示までしていただき、ありがとうございます。 とても勉強になります。 ありがとうございました。
guest

0

質問1
それでいいと思います。

質問2
今のところ無さそうですよね。

ネットを検索してみても

「FindIndex on list by linq」
https://stackoverflow.com/questions/22830497/findindex-on-list-by-linq

みたいな感じで、お遊びでやってみるのは楽しいですが、無理やり感があふれます。

実装は考えていないとのことですが、自前で拡張メソッドを書いてもいいんじゃないでしょうか。
(使い道はいろいろありそう。)

C#

1public static int FindIndex<T>(this IEnumerable<T> source, Func<T, bool> predicate) 2{ 3 int index = 0; 4 foreach (T item in source) { 5 if (predicate(item)) { 6 return index; 7 } 8 index++; 9 } 10 return -1; 11}

投稿2021/12/06 02:02

KOZ6.0

総合スコア2721

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

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

退会済みユーザー

退会済みユーザー

2021/12/06 04:08

ご回答ありがとうございます。 自前の拡張メソッドのご提示ありがとうございます。 とても勉強になります。 ありがとうございました。
guest

0

ベストアンサー

リストを使うのがスマートと思います。

C#

1string s = "aaaabc"; 2int index = s.ToList().FindIndex(c => c != 'a'); 3Console.WriteLine(index);

LINQ だけで行うのであれば(12/06 少し改善)

C#

1string s = "aaaabc"; 2//int index = s.Select((c, idx) => new { v = c == 'a' ? 0 : 1, idx }) 3// .Where(c => c.v == 1).Select(x => x.idx).DefaultIfEmpty(-1).First(); 4int index = s.Select((c, idx) => new { v = c != 'a', idx }) 5 .Where(c => c.v).Select(x => x.idx).DefaultIfEmpty(-1).First(); 6Console.WriteLine(index);

投稿2021/12/05 15:46

編集2021/12/05 23:43
lehshell

総合スコア1178

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

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

退会済みユーザー

退会済みユーザー

2021/12/06 04:07

ご回答ありがとうございます。 勉強になりました。 ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問