[[20160213060127]] 『行に有るデーターの重複の削除を関数で行いたいの』(つむつむ) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『行に有るデーターの重複の削除を関数で行いたいのですが』(つむつむ)

いつも拝見させて頂き有難うございます。

A:Eに記入されている文字をG:Kに重複を削除して表示したいのです

入力されている文字数は一定ではありません

実際のデーターはA〜Oまで15有ります

     A       B     C      D      E      F      G      H      I      J      K
 1 みかん     りんご みかん いちご        みかん りんご いちご       
 2 まぐろ ほたて     さんま うなぎ        まぐろ ほたて さんま うなぎ
 3 うさぎ うさぎ うさぎ                うさぎ
 4 ひつじ きりん かっぱ ねずみ いたち        ひつじ きりん かっぱ ねずみ いたち
 5 ヨット ヨット 潜水艦 ヨット            ヨット 潜水艦

宜しくお願い致します。

< 使用 Excel:Excel2010、使用 OS:Windows7 >


実際のデータが、どのようなものか確認したいです。

記載例だと、
A〜Eの5列で重複を除いたデータを
G〜Kの5列に記述
となってますよね。

実際のデーターはA〜Oまで15有ります とありますが、
これは、
A〜Oの15列で重複を除いたデータを
Q〜AEの15列に記述
という意味ですか?

(マリオ) 2016/02/13(土) 08:14


マリオ様
ご連絡有難うございます。

<A〜Oの15列で重複を除いたデータを
<Q〜AEの15列に記述
<という意味ですか?

その通りです。

例として、A〜Kに示しております。

宜しくお願い致します。
(つむつむ) 2016/02/13(土) 08:28


 実際は15列でも、A列から始まってることに変わりはないんですかね?

 本当にA列から始まってるのなら

 Q1 =IFERROR(INDEX($A1:$O1,SMALL(IF(($A1:$O1<>"")*(MATCH($A1:$O1&"",$A1:$O1&"",0)=COLUMN($A1:$O1)),COLUMN($A1:$O1)),COLUMN(A1))),"")

 Ctrl+Shift+Enter で確定して、右下フィルコピー

 または

 Q1 =IFERROR(INDEX($A1:$O1,SMALL(INDEX((($A1:$O1="")+(MATCH($A1:$O1&"",$A1:$O1&"",0)<>COLUMN($A1:$O1)))*10^7+COLUMN($A1:$O1),0),COLUMN(A1))),"")

 普通にEnterだけで確定して、右下フィルコピー

 データ量が多いとアレですけど、15列ぐらいならなんてことないですね。
(笑) 2016/02/13(土) 12:43

つむつむ さん

関数でのやりかたは、
(笑)さんの2つの目の「Q1=〜」で解決なのを確認しましたが、
VBAで作ってみたので、よろしかったら、どうぞ。

■STEP1
「Alt+F11」で、Visual Basicを開いて、
ThisWorkbook上で、右クリック→挿入→標準モジュール→Module1作成
Module1のコード表示欄に、下記のコードを貼り付け。
その後、ファイルの種類を「Excel 97-2003ブック」にして、適当なファイル名で保存(拡張子はxls)

****************************************************************

Option Explicit
Sub 同じ行に重複を削除したデータを貼りつける()
    Const row1 As Long = 1 'データ開始行の設定
    Const col1 As Long = 1 'データ開始列の設定(A列)
    Const col2 As Long = 15 'データ最終列の設定(O列)
    Dim myVal As Variant, c As Variant, myKey As Variant
    Dim i As Long, j As Long, row2 As Long, dic As Object

    With ActiveSheet.Range(Columns(col1), Columns(col2)) '(A〜O列)
      row2 = .Find("*", , xlFormulas, , xlByRows, xlPrevious).Row 'データ最終行を探す
    End With

    For i = row1 To row2 '行ごとの処理
        Set dic = CreateObject("Scripting.Dictionary")  '辞書の設定
        myVal = Range(Cells(i, col1), Cells(i, col2)).Value 'データを配列に格納(A〜O列)
        For Each c In myVal
            If Not c = Empty Then
               If Not dic.Exists(c) Then dic.Add c, "" 'dicへデータを格納
            End If
        Next
        myKey = dic.Keys
        For j = 0 To dic.Count - 1
            Cells(i, j + col2 + 2) = myKey(j) '(「col2」+「2」)列から書き出し
        Next j
        Set dic = Nothing '辞書データ削除
    Next i
End Sub

****************************************************************

■STEP2
保存したファイル(拡張子xls)を開いて、
適当なシートに、次のデータ(A1:O6)を貼り付けてください。

****************************************************************

	A	B	C	D	E	F	G	H	I	J	K	L	M	N	O
1		あ	あ		い	い	い		う	う	う		え	え	え
2		か	か	か		き	き	き		く	く	く		け	け
3			さ	さ			し	し			す	す		せ	せ
4	た	と		ち	て		つ	つ		て	ち		と	た	
5	の	の	の	ね	ね	ね	ぬ	ぬ	ぬ	に	に	に	な	な	な
6	は	ひ	ふ	へ	ほ	ま	み	む	め	も	や	ゆ	よ	ら	り

****************************************************************

貼り付けた後、「A列」と「1行目」を削除
セル幅が広くて見にくいので、「Ctrl+A」で全選択した後、
ホーム→書式→規定の幅→「2.75」→「OK」してください。

■STEP3
「Alt+F8」でマクロが開くので、
「同じ行に重複したデータを貼りつける」を実行する
すると、(Q1:AE6)の範囲にデータが書き込まれます。

****************************************************************

	Q	R	S	T	U	V	W	X	Y	Z	AA	AB	AC	AD	AE
1	あ	い	う	え											
2	か	き	く	け											
3	さ	し	す	せ											
4	た	と	ち	て	つ										
5	の	ね	ぬ	に	な										
6	は	ひ	ふ	へ	ほ	ま	み	む	め	も	や	ゆ	よ	ら	り

****************************************************************

(マリオ) 2016/02/13(土) 13:00

笑様
マリオ様
ご連絡有難うございました。

マリオ様に作って頂きましたVBAでも
笑様に作って頂きました配列数式や普通の数式でも
希望通りの結果を得る事が出来ました。

今回は、笑様に教えて頂きました普通の数式を使用させて頂きます。

会社の仕事が楽になります。

本当に有難うございました。
(つむつむ) 2016/02/13(土) 13:30


 補足というか訂正というか。

 > Q1 =IFERROR(INDEX($A1:$O1,SMALL(INDEX((($A1:$O1="")+(MATCH($A1:$O1&"",$A1:$O1&"",0)<>COLUMN($A1:$O1)))*10^7+COLUMN($A1:$O1),0),COLUMN(A1))),"")
 
「10^7」としてるところ、ありますよね。
 最初、INDEXの範囲を、INDEX(1:1,・・・ と「1:1」(行全体)にしていたので
 テキトーに 10^7 にしました(Excel2010の最大列数以上の数値ってことです)

 INDEX($A1:$O1,・・・ と範囲を限定した場合、10^7のままでも問題はないですけど、
 この場合は15列なので「15」以上の数値なら何でもいいです。

 たとえば「100」でもオッケー

 Q1 =IFERROR(INDEX($A1:$O1,SMALL(INDEX((($A1:$O1="")+(MATCH($A1:$O1&"",$A1:$O1&"",0)<>COLUMN($A1:$O1)))*100+COLUMN($A1:$O1),0),COLUMN(A1))),"")

 以上、参考まで。
(笑) 2016/02/13(土) 14:07

コードに不具合がありました。【1】の追加,【2】の修正

'【1】
'■(追加)はじめに、Q〜AE列のデータ(値、数式)を消去
'A〜O列のデータが書き換えられた後に、プログラムを実行すると、
'前に書き込んだデータが残っていたりするので。
ActiveSheet.Range(Columns(col2 + 2), Columns((col2 + 2) + (col2 - col1 + 1))).ClearContents

'【2】
'If Not c = Empty Then'■(修正前)
If c <> "" Then '■(修正後)0のデータを書き出すようにするため

***********************************************************************************************

Option Explicit
Sub 同じ行に重複を削除したデータを貼りつける()
    Const row1 As Long = 1 'データ開始行の設定
    Const col1 As Long = 1 'データ開始列の設定(A列)
    Const col2 As Long = 15 'データ最終列の設定(O列)
    Dim myVal As Variant, c As Variant, myKey As Variant
    Dim i As Long, j As Long, row2 As Long, dic As Object

    '■(追加)はじめに、Q〜AE列のデータ(値、数式)を消去
    ActiveSheet.Range(Columns(col2 + 2), Columns((col2 + 2) + (col2 - col1 + 1))).ClearContents

    With ActiveSheet.Range(Columns(col1), Columns(col2)) '(A〜O列)
      row2 = .Find("*", , xlFormulas, , xlByRows, xlPrevious).Row 'データ最終行を探す
    End With

    For i = row1 To row2 '行ごとの処理
        Set dic = CreateObject("Scripting.Dictionary")  '辞書の設定
        myVal = Range(Cells(i, col1), Cells(i, col2)).Value 'データを配列に格納(A〜O列)
        For Each c In myVal
           'If Not c = Empty Then'■(修正前)
            If c <> "" Then '■(修正後)0のデータを書き出すようにするため
               If Not dic.Exists(c) Then dic.Add c, "" 'dicへデータを格納
            End If
        Next
        myKey = dic.Keys
        For j = 0 To dic.Count - 1
            Cells(i, j + col2 + 2) = myKey(j) '(「col2」+「2」)列から書き出し
        Next j
        Set dic = Nothing '辞書データ削除
    Next i
End Sub

***********************************************************************************************

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
どなたか、次の【3】のエラー対策を教えてください。ん〜。
'【3】
'A〜O列に何もデータがないとき、「row2」(データ最終行を探す)でエラーとなる
'→エラー対策したい
'A〜O列に何もデータ(数値の0も含む!)がないとき、Exit Subしたい
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

あと、(笑)さんの関数(数式)に関してなんですが、
列の最大値を数式で求められないですかね?VBAだと、Columns.Countですが

(マリオ) 2016/02/13(土) 16:55


笑様
ご連絡有難うございます。

式の補足説明有難うございます。
勉強になります。

マリオ様
ご連絡有難うございます。

>A〜O列に何もデータがないとき、「row2」(データ最終行を探す)でエラーとなる
気が付きませんでしたが、確かにA〜Oにデーターが無い場合不具合となる様です。
最後までのフォロー有難うございます。
(つむつむ) 2016/02/13(土) 17:24


 全部見ているわけではありませんが・・・。
 >'A〜O列に何もデータがないとき、「row2」(データ最終行を探す)でエラーとなる 
 Findメソッドは、そもそも検索文字があるRangeオブジェクトを返すメソッドです。
 見つかったRangeオブジェクトが返されるから、有効なRowプロパティが返されるのです。

 dim frng as range

 ・
 ・

 set frng=.Find("*", , xlFormulas, , xlByRows, xlPrevious)
 if not frng is nothing then row2=frng.row

 なのでしょうねえ
 尚、ロジック全体は見ていませんので、提示したコードでエラー回避は判断してください

 >VBAだと、Columns.Countですが

 数式にも columns(セル範囲) というのがありますから 調べてみてください

(ichnose) 2016/02/13(土) 17:52


ichnose さん
■数式にも columns(セル範囲) というのがありますから
■調べてみてください

ありがとうございます。
数式のcolumns(セル範囲)調べてみます。あったかな〜。

■dim frng as range
■set frng=.Find("*", , xlFormulas, , xlByRows, xlPrevious)
■if not frng is nothing then row2=frng.row

やってみたのですが、frngでオブジェクトが何チャラ〜でエラーに
なっちゃいました。
初歩的なやり方ですが、row2の前の行に、On Error GoTo myError
を置いてみました。

(マリオ) 2016/02/13(土) 18:37


■つむつむ さん
こちらも勉強になります。

次のコード(***〜***間)を、
「Module1」ではなく、「Sheet1」のコード表示欄に貼り付けてください。

****************************************************************

Private Sub Worksheet_SelectionChange(ByVal Target As Range)

    Const row1 As Long = 1 'データ開始行の設定
    Const col1 As Long = 1 'データ開始列の設定(A列)
    Const col2 As Long = 15 'データ最終列の設定(O列)
    Dim myVal As Variant, c As Variant, myKey As Variant
    Dim i As Long, j As Long, row2 As Long, dic As Object

    If Intersect(Target, Range(Columns(col1), Columns(col2))) Is _
       Nothing Then End '(A〜O列)以外であれば終了’★★★

    '■(追加)はじめに、Q〜AE列のデータ(値、数式)を消去
    ActiveSheet.Range(Columns(col2 + 2), Columns((col2 + 2) + (col2 - col1 + 1))).ClearContents

    With ActiveSheet.Range(Columns(col1), Columns(col2)) '(A〜O列)
      On Error GoTo myError '■■■追加
      row2 = .Find("*", , xlFormulas, , xlByRows, xlPrevious).Row 'データ最終行を探す
    End With

    For i = row1 To row2 '行ごとの処理
        Set dic = CreateObject("Scripting.Dictionary")  '辞書の設定
        myVal = Range(Cells(i, col1), Cells(i, col2)).Value 'データを配列に格納(A〜O列)
        For Each c In myVal
           'If Not c = Empty Then'■(修正前)
            If c <> "" Then '■(修正後)0のデータを書き出すようにするため
               If Not dic.Exists(c) Then dic.Add c, "" 'dicへデータを格納
            End If
        Next
        myKey = dic.Keys
        For j = 0 To dic.Count - 1
            Cells(i, j + col2 + 2) = myKey(j) '(「col2」+「2」)列から書き出し
        Next j
        'Set dic = Nothing '辞書データ削除'■■■削除
    Next i

myError: '■■■追加

    'If Err.Number <> 0 Then MsgBox "A〜O列にはデータが1つもありません" '■■■追加(表示がうざそうなのでコメント化)
    Set dic = Nothing '辞書データ削除'■■■追加
    ActiveSheet.Range(Columns(col1), Columns((col2 + 2) + (col2 - col1 + 1))) _
                                        .AutoFit 'A〜AE列のセル幅を自動調整

End Sub

****************************************************************

'【3】の対策(下の3か所)

*************************************

row2 =〜の前の行に、次を追加
On Error GoTo myError '■■■追加

*************************************

Next iの前の行を削除(先頭に「'」をつける)
'Set dic = Nothing '辞書データ削除'■■■削除

*************************************

最後のSubの前に、次の4行を追加
myError: '■■■追加
    'If Err.Number <> 0 Then MsgBox "A〜O列にはデータが1つもありません" '■■■追加
    Set dic = Nothing '辞書データ削除'■■■追加
ActiveSheet.Range(Columns(col1), Columns((col2 + 2) + (col2 - col1 + 1))) _
                                        .AutoFit 'A〜AE列のセル幅を自動調整

*************************************

今まで、「Module1」に記述してきましたが、
これだと、いちいち、プログラムを実行しなくてはいけない。
関数を使っているときのように、(A〜O列)の値が変わったときに、
即座に(Q〜AE列)の値を変更させたい。

そこで、
データを貼りつけたシート(例えば、「Sheet1」)に
次のコードを記述

今までのコードの
■Sub 同じ行に重複を削除したデータを貼りつける()
だったところが、
■Private Sub Worksheet_SelectionChange(ByVal Target As Range)
に変わっています。
また、
If Intersect(Target, Range(Columns(col1), Columns(col2))) Is _

       Nothing Then End '(A〜O列)以外であれば終了’★★★
を追加しています。
(マリオ) 2016/02/13(土) 18:38

■つむつむ さんへ
最後にしますが、次の2行を追加すると、表示がちらつきにくくなります。

■Dim i As Long, j As Long, row2 As Long, dic As Object
の次の行に追加
Application.ScreenUpdating = False '画面の描画をOFFにします

■End Sub
の前の行に追加
Application.ScreenUpdating = True '画面の描画をONに戻します

(マリオ) 2016/02/13(土) 19:33


 >dim frng as range
 >・
 >・
 >set frng=.Find("*", , xlFormulas, , xlByRows, xlPrevious)
 >if not frng is nothing then row2=frng.row

 私が提示したコード、frngという変数は、3行に書かれています。
 >frngでオブジェクトが何チャラ〜でエラーに 

 どの行で どのようなエラーが発生したのかを正確に記述してください。

 set frng=.Find("*", , xlFormulas, , xlByRows, xlPrevious)
 ここでエラーなのかなあ と想像していますが・・・。

 まさか 最後に .Rowを残しているなんてことは ないですよね?

 On Errorを使った処理、私は それでも良いと思っています。

 が、その場合、どのようなエラーを拾うのか?を規定する必要があります。

 Findメソッドは、検索文字列が見つからない場合は、Nothingが返ります。

 見つかれば、そのセルのRangeオブジェクトが返ります。

 見つからなければ、Nothingが返る そのNothingに対して Rowプロパティを取得しようとするから、
 エラーになる、そのトラップをOn Errorで拾う と言うならわかります。

 が、それを分けただけの

  >set frng=.Find("*", , xlFormulas, , xlByRows, xlPrevious)

 ではエラーに成ると記述された、ということは、他にも(検索文字が見つからない以外)エラー原因が潜んでいると想像してしまいます。

 On Errorを使うなら、この辺りを解決された方がよいと思いますよ!!

 因みにOn Error を使うなら 可能な限り適応範囲を限定する方が良いので

    With ActiveSheet.Range(Columns(col1), Columns(col2)) '(A〜O列)
      On Error GoTo myError '■■■追加
      row2 = .Find("*", , xlFormulas, , xlByRows, xlPrevious).Row 'データ最終行を探す
      on error goto 0    
    End With

 とした方が良いと思いますよ

(ichnose) 2016/02/13(土) 20:13


ichnose さん

失礼いたしましたm(_ _)mぺこぺこぺこ
■まさか 最後に .Rowを残しているなんてことは ないですよね?
それしか考えられません。凡ミスです!
下記(***以下)のコードで問題ありませんでした。

■On error goto 0
後からコードを見直した時に、エラーが起こる箇所
が分かりやすい方がいいですね。ご指摘いただき、ありがとうございます。
m(_ _)mぺこぺこぺこ
つむつむ さんも、見てましたら、On error goto 0を追記してください。

On Error GoTo myError
row2 = .Find("*", , xlFormulas, , xlByRows, xlPrevious).Row
On error goto 0

■数式にも columns(セル範囲) というのがありますから 調べてみてください
やっぱり、関数(数式):columns(セル範囲)で、
列の最大値の求め方が分かりません。

教えてくださ〜い。
Excel2003の列数は256、
Excel2007の列数16,384

****************************************************************

Dim frng As Range
'(省略)
    With ActiveSheet.Range(Columns(col1), Columns(col2))
      Set frng = .Find("*", , xlFormulas, , xlByRows, xlPrevious)
      If Not frng Is Nothing Then
         row2 = frng.Row
      Else
         GoTo myError
      End If
    End With
'(省略)
myError: 
    If frng Is Nothing Then MsgBox "A〜O列にはデータが1つもありません"
    Set frng = Nothing

(マリオ) 2016/02/13(土) 22:13


ichnose様
ご連絡有難うございます。

マリオ様
VBAが進化していてビックリしました。
データーを張付けただけで計算されるんですね

有難うございました。
(つむつむ) 2016/02/14(日) 07:01


 To マリオさん

 目に留まらないかもしれませんし、間違いということではないので、いらぬおせっかいですけど。

    For i = row1 To row2 '行ごとの処理
        Set dic = CreateObject("Scripting.Dictionary")  '辞書の設定

 ループのたびに新規オブジェクトとして生成してもよろしいんですが

 Set dic = CreateObject("Scripting.Dictionary")  '辞書の設定
 For i = row1 To row2 '行ごとの処理
    dic.RemoveAll

 という使い方もできます。ご参考まで。

(β) 2016/02/14(日) 08:15


こんにちは。

■マリオさん
>やっぱり、関数(数式):columns(セル範囲)で、
> 列の最大値の求め方が分かりません。

あれ、VBAで書くときだって
特定のシート.Columns.Count もしくは
セル範囲.Columns.Count じゃないですか。

セル範囲.Columns.Countと書いて256なりを返すための「セル範囲」は
どこでしょうということですよね。Cells以外で。

■つむつむさん
もしかしてコメントをいただく場合
わたしには様は勘弁してくださいな。

( 佳 ) 2016/02/14(日) 09:16


■βさん
Q&Aさろん(VBA)のβさんですかね(^^♪?
どうも

>dic.RemoveAll
知らなかったです。ありがとうございます。

■佳さん
?VBAじゃなくて、エクセルの数式欄でのことなんですが、
例えば、「A1」セルの数式欄に、
「=COLUMN()」で「1」
「=COLUMN(B1)」で「2」

Excel2013で、
xlsxのファイルの「A1」セルに、
「=COLUMNS($1:$1048576)」で「16384」←最大列数
xlsのファイルの「A1」セルに、
「=COLUMNS($1:$65536)」で「256」←最大列数

xlsxとxlsの区別なしに、
「$1:$1048576」と「$1:$65536」を使わずに、
COLUMNまたは、COLUMNS関数を使って、
最大列数を求めたいのですが。
(マリオ) 2016/02/14(日) 10:25


こんにちは。

>?VBAじゃなくて、エクセルの数式欄でのことなんですが、
難しいことをするときは、使い慣れた道具を使うべし、と言います。
マリオさんにとっては、VBA的発想のほうが考えやすいかなと思ったので
そういう文脈でコメントしました。

>「=COLUMNS($1:$65536)
256を得るために、65536行までの全行が必要ですか?

まあ、Excel2013でもこの式で16384が返るはずなので、
あながち間違いとはいえませんが (^^
VBAでそう書いたら なぜマジックナンバーを使うのかと
ツッコミが入りそうです。

( 佳 ) 2016/02/14(日) 10:55


 佳さんから説明がありますが蛇足で。

 Columnsプロパティの親(Parent) は、佳さん指摘の通り、シートオブジェクトです。
 残念ながら Application ではありません。

 で、親(シート)を省略すると、シートモジュールでは、そのコードが書かれたシート、
 そのほかのモジュールでは、たまたま、その時にアクティブになっているブックのアクティブシートになります。

 ですから、xlsxブックが最前面にあれば Columns.Count は 16384 、xlsブックが最前面にあれば 256 になります。
 これは Rows に関しても同様です。

(β) 2016/02/14(日) 11:24


■佳 さん
■65536行までの全行が必要ですか?

そうか、ファイルが(xls、xlsxどちらでも)何であろうと、数式
=COLUMNS($1:$1)
で最大列数、取得できますね(^^♪

セル範囲で、「$A:$A」はよく使いますが、
「$1:$1」に気づかなかった〜

(マリオ) 2016/02/14(日) 14:11


■笑さんの
Q1 =IFERROR(INDEX($A1:$O1,SMALL(INDEX((($A1:$O1="")+(MATCH($A1:$O1&"",$A1:$O1&"",0)<>COLUMN($A1:$O1)))*10^7+COLUMN($A1:$O1),0),COLUMN(A1))),"")

の数式の「10^7」のとこですが、「COLUMNS($1:$1)」と置き換えてもいいんですね。

Q1 =IFERROR(INDEX($A1:$O1,SMALL(INDEX((($A1:$O1="")+(MATCH($A1:$O1&"",$A1:$O1&"",0)<>COLUMN($A1:$O1)))*COLUMNS($1:$1)+COLUMN($A1:$O1),0),COLUMN(A1))),"")

数式の意味、後で考えよ(^^♪
だれか説明して〜
(マリオ) 2016/02/14(日) 14:15


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.