
[code]

    text_1 = 'hogeあいうえお'
    text_2 = 'hogeかきくけこ'
    text_3 = 'hogeさしすせそ'
    
    期待値 => 'hoge'
    
[/code]

* * *
[code]

    text_1 = 'hogeあいうえお'
    text_2 = 'hogeあいうえおかきくけこ'
    text_3 = 'hogeあいうさしすせそ'
    
    期待値 => 'hogeあいう'
    
[/code]

「前方の一致部分」に限ります。

これをrubyで実現したいのですがどうしたらよいのか全くわかりません。  
わかる方いましたらお願いします。


CC BY-SA 4.0
2016-07-14T09:53:02.573
27617
2019-09-25T08:03:48.323
2019-09-25T08:03:48.323
2238
9149
question
ruby
複数の文字列から同じ部分を抜き出す方法


（自分はRubyに不慣れなので参考程度に考えてください。）

各行の処理についてはコメントを見てください。また最初に文字列を配列に格納するところは省略しています。  

[code]

    # coding: utf-8
    # words = ['hogeあいうえお','hogeかきくけこ','hogeさしすせそ']
    words = ['hogeあいうえお','hogeあいうえおかきくけこ','hogeあいうさしすせそ']
    # words = ['a','a','a']
    # words = ['a','a','i']
    # words = ['','a','a']
    # words = ['','','']
    # words = []
    m = words.map{|v|v.length}.min
    p words.map{|v| v[0,m].chars.to_a} #一番短い文字列の長さに合わせる
       .transpose # 転置
       .take_while{|v| v.all?{|i|i==v[0]}} # 全ての要素が等しい配列が続く限り取り出す
       .map {|v| v.first} # 各配列の先頭を取り出す
       .inject(""){|str,c| str+c} # 文字列にまとめる
    
[/code]

[code]

    $ ruby commonseq.rb
    "hogeあいう"
    
[/code]

`transpose`の返す配列は自身が空、もしくは空でない要素を持つので、後続の操作は安全です。


CC BY-SA 3.0
2016-07-14T12:38:42.490
27625
2016-07-14T13:40:45.957
2016-07-14T13:40:45.957
13199
13199
27617
answer


`differ`というライブラリを使うのが簡単です。

[code]

    $ gem install differ
    
[/code]

[code]

    require 'differ'
    
    def diff(strs)
      strs.reduce do |s1, s2|
        s = Differ.diff_by_char(s1,s2).to_s
        s[0..s.index('{')-1]
      end
    end
    
    list1 = ['hogeあいうえお','hogeかきくけこ','hogeさしすせそ']
    list2 = ['hogeあいうえお','hogeあいうえおかきくけこ','hogeあいうさしすせそ']
    
    
    puts diff(list1) # hoge
    puts diff(list2) # hogeあいう
    
[/code]

簡単に解説すると、`Differ.diff_by_char()`というメソッドは以下のように２つの文字列を文字単位で比較し、差分を返します。

[code]

    require 'differ'
    
    puts Differ.diff_by_char('abc','abd').to_s
    # ab{"d" >> "c"}
    
    puts Differ.diff_by_char('ab','abc').to_s
    # ab{-"c"}
    
[/code]

そのため、比較対象の文字列に`{`が入っていないことを前提とすると、差分のうち`{`が現れる前までの文字列を前方一致箇所とみなすことができます。

### 追記:

以下のように書き直すと、対象文字列に`{`が含まれていた場合でも正確に対応できます。

[code]

    require 'differ'
    
    def diff(strs)
      strs.reduce do |s1, s2|
        diff = Differ.diff_by_char(s1,s2)
        raw = diff.send(:raw_array)
        (raw[0].is_a? String)? raw[0]: ''
      end
    end
    
    list1 = ['hoge{あいうえお','hoge{かきくけこ','hoge{さしすせそ']
    list2 = ['hogeあいうえお','hogeあいうえおかきくけこ','hogeあいうさしすせそ']
    
    
    puts diff(list1) # hoge{
    puts diff(list2) # hogeあいう
    
[/code]


CC BY-SA 3.0
2016-07-18T16:55:37.060
27715
2016-07-19T02:09:13.717
2016-07-19T02:09:13.717
17181
17181
27617
answer