動的配列からNULLを取り去る関数


ゆきんこ  2007-02-09 18:06:39  No: 97972

VB6で動的1次元配列A()に、ある処理のから得られた結果をNullを含むのデータが格納されるとします。

(例えば下記)
A()=A,G,Null,K,Null,Null,D,R,W,Q

UBound(A())=9

このA()からNullを取り去った配列B()を得たいのですが、

(例えば下記)
B()=A,G,K,D,R,W,Q

UBound(B())=6

For〜Nextなど、ループを使わずに、A()のNull一気に取り去った
結果B()を得るような関数はありませんか?


K.J.K.  2007-02-09 19:18:55  No: 97973

ありません。


ゆきんこ  2007-02-09 20:02:54  No: 97974

動的配列の要素を静的配列に入れ替えて繰り返しでチェックして入れ替えるのが、スマートではないので、繰り返しを使わない処理はないものかと思っています。KJKさん以外の方、お願いします。


我龍院  2007-02-09 20:43:10  No: 97975

ありません。


レンジ  2007-02-09 21:07:22  No: 97976

言語がPerlなどなら参照する方法はありますが
VBではループを使う方法しかないように思います。

なぜ、ループを使いたくないかの
理由付けが分かるといいのですが

>ある処理のから得られた結果をNullを含むのデータが格納されるとします。
→私がloopを使いたくない、
nullをチェックしたいというのであれば
CSV形式の文字列で格納します。


我龍院  2007-02-09 21:18:28  No: 97977

まあ提示された配列なら、
Dim b() As String
b = Split(Replace(Join(a, Chr(0)), Chr(0) & Chr(0), ""), Chr(0))
これで出来ないことは無いですが、これだけの情報では....
マナーも....


魔界の仮面弁士  2007-02-09 21:21:13  No: 97978

KJKさんではなく、K.J.K.さんですよね。

> 動的配列の要素を静的配列に入れ替えて繰り返しで
なぜ、ここで静的配列が必要になってくるのかが分りませんでした。
そのまま、「動的配列→別の動的配列」で良いと思いますけど…。
(もしくは、配列ではなく、コレクションを使うとか)

静的配列へと入れ替えている理由がわからないので、まずは、
現状の「スマートではない」というコードを見せて頂けませんか?
その内容次第では、代替案が提供できるかもしれません。


> A()=A,G,Null,K,Null,Null,D,R,W,Q
これは文法違反ですよね。具体的には、どういう意味でしょうか?

そもそも、A の型が何であるかすら分りませんし、
「Null」というデータが何を意味しているのかも不明瞭です。

# "Null" という文字列とか(String型)
# vbNullString だとか(String型)
# vbNullChar だとか(String型)
# 空文字列だとか(String型)
# Null値だとか(Variant型)
# Empty値だとか(Variant型)
# Nothingだとか(Object型)


> 繰り返しを使わない処理はないものかと
内容次第では代替案もあるかと思います。
(たとえば文字列系なら、RegExp または Join + Replace した結果を Split するとか)

ですが、汎用的な手法となれば、基本的には A をループさせて
処理するしか無いと思いますよ。(さほど面倒な作業でも無いですよね?)


ゆきんこ  2007-02-10 20:22:14  No: 97979

皆様回答ありがとうございます。
文字列(string型)の処理について質問させていただいたのですが、
ループを使いたくないのではなくて、ループを使わないですむような簡単な方法はないかと言いたかったのです。言葉が足りずすみません。

>現状の「スマートではない」というコードを見せて頂けませんか?

イメージだけで言ってたので、現状何もなかったのですが、
ループでつくるとしたら下記です。

Dim A() As String, B() As String
Dim i As Integer, j As Integer

A() = Split("AXGXXKXXXDXRXWXQ", "X", -1, vbTextCompare)
If UBound(A()) = -1 Then Exit Sub

ReDim Preserve A(UBound(A()))
ReDim B(0)

j = 0
For i = 0 To UBound(A())
 If Not A(i) = "" Then
   ReDim Preserve B(j)
   B(j) = A(i)
   j = j + 1
  End If
Next i
    
Debug.Print "UBound(a())="; UBound(A())
Debug.Print "UBound(b())="; UBound(B())

なんか、スマートじゃない気がするのですが・・・
静的配列の件ですが、splitのあと、Redimを使うと、静的配列になると
思っていたのですが、調べたら違うみたいですね。
初心者ですみません。

これが関数で一発でできないかと思いまして、我龍院さんや、魔界の仮面弁士さんの発言のような、join、replace、split関数を駆使してできるものであればそのようにしたいのですが(我龍院さんの方法も、うなぜかうまく動かず)・・・・・・・・・・


魔界の仮面弁士  2007-02-10 22:06:13  No: 97980

> 文字列(string型)の処理について質問させていただいたのですが、
最初からそう書いていただければ、具体的なコードを例示できたのに……。


> ループを使わないですむような簡単な方法はないかと
ループ処理させるのが一般的かと。


> ReDim Preserve A(UBound(A()))
これは、何のために行っているのでしょうか?


> ReDim B(0)
(中略)
>   ReDim Preserve B(j)
ループ中で毎回 ReDim するのはお奨めしません。

ReDim Preserve では、現在の配列サイズを変更するのではなく、
新たなサイズの配列を作り、そこに内容をコピーすることで、
配列が拡縮されているかのようにみせかけているため、
あまり効率の良い処理ではないからです。


この場合は、

  ReDim B(UBound(A))
  j = - 1
  For i = 0 To UBound(A)
    If A(i) <> "" Then
      j = j + 1
      B(j) = A(i)
    End If
  Next
  ReDim Preserve B(j)

のようにして、Preserve する回数を減らした方が良いでしょう。

また、配列に拘らないなら、Dictionary や Collection などを使うのも
便利かと思います。要素の最大値を気にする必要がありませんので。


> これが関数で一発でできないかと思いまして
空の文字列を潰したいのであれば、
  1. 「データ中に絶対に現れない文字列」を定義。
  2. (1)で定義した文字列を使って、配列を Join で連結。
  3. (1)で定義した文字列が、2個以上連続した部分があったら、1個に置換。
  4. (2)で作成した連結文字列を、(1)で定義した文字列で Split する。
のようにすることができます。

たとえば、こんな感じで。
  Dim X As String
  X = vbNullChar  'データ中に絶対に現れない文字列
  Dim B() As String
  B = Split(Replace(Replace(Join(A, X), X & X, X), X & X, X), X)

空の文字列ではなく、"Null" などの「特定の文字列」を潰したい場合も、
これの応用で処理できるでしょう。


ですが、こうした Join / Replace / Split 法ですと、たとえば、
vbNullString なデータが "" に変化してしまうなどの弊害がありますし、
そもそも、文字列以外の処理には使えないという欠点もあります。
なによりも、後からこのコードを見た時に、何を行っているのかが
理解し難い(保守し難い)コードとなってしまいますので、あまり
積極的に利用すべき手法では無いかと。

個人的には、素直にループで処理することをお奨めします。


魔界の仮面弁士  2007-02-10 22:10:00  No: 97981

ん…なんか違う。

> 1. 「データ中に絶対に現れない文字列」を定義。
> 2. (1)で定義した文字列を使って、配列を Join で連結。
> 3. (1)で定義した文字列が、2個以上連続した部分があったら、1個に置換。
> 4. (2)で作成した連結文字列を、(1)で定義した文字列で Split する。

1. 「データ中に絶対に現れない文字列」を定義。
2. (1)で定義した文字列を使って、配列を Join で連結。
3. (2)で作成した連結文字列の中に、(1)で定義した文字列が
  2個以上連続した部分があったら、1個に置換。
4. (3)で置換された文字列を、(1)で定義した文字列で Split する。

かな。


ねここ  2007-02-11 01:40:01  No: 97982

まぁ、腹が立つのは分かりますが
回答を複数出してもらってあなたが選択する趣旨の
掲示板ではないので
>KJKさん以外の方、お願いします。
みたいなのはやめたほうがよいかと。


今回は求める回答ではないとしても
次に質問したときには解決の手助けをしてくれるかも
しれません。

#多分このレスだけであなたへの回答者が数人減ってたり
#するかもしれません。


K・今川  2007-02-11 13:32:39  No: 97983

Private Sub Command1_Click()

    Dim a As Variant
    Dim b As Variant
    
    a = Array("a", "G", Null, "K", Null, Null, "D", "R", "W", "Q")
    
    b = Filter(a, Null, False)
    
    Text1(0).Text = UBound(a)
    Text1(1).Text = UBound(b)

End Sub

通常の文字列であれば、上記のプログラムでゆきんこさんの
望むような処理が出来るのですがNULL値があると、

    b = Filter(a, Null, False)

のところで

      Null の使い方が不正です。(Error 94)

のエラーが出てしまいうまくいきません。
詳しくは、HELPでFilter関数を調べてください。
ちなみにJoin関数でも同じエラーが出ます。

結局、今回の場合ループ処理をするしか方法はないようですね。


matsu  2007-02-12 15:24:20  No: 97984

>VB6で動的1次元配列A()に、ある処理のから得られた結果をNullを含むのデータが格納されるとします。

Aに結果を格納する時点でNullチェックは問題があるのですか?


GOD  2007-02-12 21:15:56  No: 97985

本当に A も B も String型にする必要あるの?
↓じゃダメなのかな?

    Dim a() As Byte
    Dim b() As Byte
    a = StrConv("AG" & vbNullChar & "K" & vbNullChar & _
                vbNullChar & "D" & "R" & "W" & "Q", vbFromUnicode)
    b = StrConv(Replace(StrConv(a, vbUnicode), vbNullChar, ""), vbFromUnicode)
    Debug.Print UBound(b)


我龍院  2007-02-13 08:31:11  No: 97986

>我龍院さんの方法も、うなぜかうまく動かず
上手く動かないはずです、私はNullをvbNullCharと決めつけて
書いていますから。(^^;

>なんか、スマートじゃない気がするのですが・・・
プログラムの基本は条件分岐とループでは。
良いコードとは、
1、スピードが速い。(処理量が多い場合)
2、メモリーの消費量が少ない。(非力なシステムの場合)
3、読みやすい。(=構造が簡単)
スピードの点では、ほかの配列にコピーする方法が速そうです。
Replaceの方法は、Join、Replaceは速いのですが、Splitが
遅い様に思われます、自分で書いて何ですがお勧めしません。

コピーの欠点は、データーが多い場合、コピーデーターの配列を幾つ
確保したら良いか一意に決めかねる点です。
処理量が多く且つふるい落とすデーターが多い場合は、元の配列と
同じサイズの配列を確保することはシステムに負担をかけます。
配列を1000個位づつ用意して足りなくなった時点で補充する方法も
有ります。この部分を複雑にすると可読性が落ちます。

メモリーの消費量からすると、その配列を使って左詰の方法が有ります。
どのような場合にも利用可能で、実は個人的にはこの方法がお勧めです。
配列の左が削除データーなら右の値を詰めていく方法です、
速度は速いでしょう、ただしどこまで詰めたかと、どこを比較しているか
と言う2つのポインターを制御しますから、可読性は良くありません。
メモリーが限られている、機器組み込み用のソフトならこの方法しか
有りません。


※作成する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。







   このエントリーをはてなブックマークに追加