TreeView内でノードの検索をするには?


質問者  2013-08-21 21:19:13  No: 45123

お世話になります。
データをツリーに1件ずつ追加していく場合
重複箇所は1つにまとめたい(下記分かりづらいですがこんな感じです)場合、
・A−B−C ---①
・A−B−D ---②

A−B−C
    ∟D
②追加のタイミングでA,Bと重複していることが分かれば
その下に追加データをぶらさげるようにしたいのですが、
実装方法が思い浮かばず。。ヒントで構わないのですが、②追加のタイミングで
既に生成されている①に重複箇所があることを知る方法はありますか?
分かりづらくすみません。


回答者  2013-08-23 01:12:37  No: 45124

まさに、TreeViewの得意とするところです。
このようなものの表示のために、TreeViewがあるとっいも過言ではないと思います。
(これじゃ、解決ににらないか)
「重複箇所」という概念ではなく、表示を優先するフィールドでソートして、データ側で回せば、ハイ解決です。
そもそも、データ構造から考えてみたらいいと思います。


傍観者  2013-08-23 07:16:41  No: 45125

↑なにいっているのかわかんないや


  2013-08-23 16:35:24  No: 45126

回答を見てみましたが、意味の把握が難しいものになっていますね。
  意味が通じる程度に端折るのはよいと思いますが、質問者が解決につなげ
られる程度の書き方をしてあげると、ここの掲示板も有効活用できるのでは
ないかと思います。

  今少し見てみましたが、TreeView側で処理を行うのは、多少面倒な感じ
ですね。ですので、もともと、データを作成するときに作っている元データ
を元に検索するとよいのではないかと思います。

例えば、例に上がっているものです。
・A−B−C
・A−B−D

こういうものを、文字列データとして保存しているのであれば、
これにより、Aはあるか、A-Bはあるか、などを探してはどうでしょうか。
正規表現検索などを組み合わせて、行頭から探す、あいまい検索をする、
などとすると、少し簡単になるかもしれません。

もしこのようなデータを保持していないのであれば、
TTreeView1.SaveToStream によって、ツリー構造をテキスト形式で出力
できたと記憶しています。これをTStringListに読み出し、上のような
検索を行うことで、実現できるのではないかと思います。

試していませんが、ヒントになれば幸いです。


tor  2013-08-24 06:01:33  No: 45127

質問者さんは、そもそも重複を考えずに普通にデータをツリーに追加する場合どうしているのでしょうか。

例えば A-B-C というデータがあったら、これを分解して
1. ルートの下に A というノードを新しく作って追加する。
2. 1で作った A の下に、B というノードを新しく作って追加する。
3. 2で作った B の下に、C というノードを新しく作って追加する。
おそらくこうですよね。

重複をチェックするには、この手順を少しアレンジします。
例えばデータが A-B-D だったら、これを分解して
1. ルートの下に A というノードがあるか調べる。
  あればそれを採用し、なければ新しく A を作って追加する。
2. 1で見つけたか作った A の下に、B というノードがあるか調べる。
  あればそれを採用し、なければ新しく B を作って追加する。
3. 2で見つけたか作った B の下に、D というノードがあるか調べる。
  あればそれを採用し、なければ新しく D を作って追加する。
こうなります。

で、タイトルと考え合わせると、この手順のうち「○○の下に××があるか調べる」ところで悩んでいるのでしょうか?
だとしたら、TTreeNodeのItem[index]プロパティで子ノードを順番に調べることができます。

// 現在のノード(current_node)の下から 'B' を探す例
next_node := nil;
for i := 0 to current_node.Count - 1 do
begin
  if current_node.Item[i].Text = 'B' then
  begin
    next_node := current_node.Item[i]; // 見つかったのでこれを採用
    break; // 残りは調べる必要なし
  end;
end;
// 見つからなかったら新しく作る
if next_node = nil then
  next_node := TreeView1.AddChild(current_node, 'B');


Sara  2013-08-26 02:52:08  No: 45128

面白そうだったので、ちょっと考えてみました。

ジェネリクスを使っているので、Delphi2009以降ですが・・・(^^;

データの区切りは'-'で行っていますので、a-b-c-dのように入力してください。
階層を増やすのであれば、適宜Arrayを変更してください。
エラーチェック等はありません。

TTreeViewとTEditとTButtonを各1個配置

Dic : Array[0..3] of TDictionary<String,TObject>;

procedure TForm1.Button1Click(Sender: TObject);
var Node,aNode,pNode : TTreeNode;
    i,j,k,Lv : Integer;
    Str,Str2,Key : String;
begin
    Node := TreeView1.TopItem;
    Str := Edit1.Text;
    Lv := 0;
    repeat
       i := Pos('-',Str);
       pNode := Node;
       Key := Node.Text;
       while pNode.Level > 0 do begin
          pNode := pNode.Parent;
          Key := pNode.Text + '-' + Key;
       end;
       if i > 0 then begin
          Str2 := Copy(Str,1,i - 1);
          if not Dic[Lv].ContainsKey(Key + Str2) then begin
             aNode := TreeView1.Items.AddChildFirst(Node,Str2);
             Dic[Lv].Add(Key + Str2,TObject(aNode));
             Node := aNode;
          end
          else begin
            Node := TTreeNode(Dic[Lv].Items[Key + Str2]);
          end;
          Str := Copy(Str,i+1,Length(Str));
          Inc(Lv);
       end
       else begin
         if not Dic[Lv].ContainsKey(Key + Str) then begin
           aNode := TreeView1.Items.AddChild(Node,Str);
           Dic[Lv].Add(Key + Str,TObject(aNode));
         end;
       end;
    until i = 0;
end;

procedure TForm1.FormCreate(Sender: TObject);
var i : Integer;
begin
   TreeView1.Items.Add(nil,'Root');
   for i := 0 to 3 do begin
      Dic[i] := TDictionary<String,TObject>.Create;
   end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var i : Integer;
begin
   for i := 0 to 3 do begin
      Dic[i].Clear;
      Dic[i].Free;
   end;
end;


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

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






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