[あすかぜ・ねっと]あすかぜ・ねっと自作ソフトCassava > サポート掲示板

Cassava Editor サポート掲示板

スレッド一覧に戻る返信

[626] 任意の文字列と重複した行だけを残したい

奈々氏 [2021/05/27 10:06:12]

いつも大変お世話になっています。

[617]こちらのマクロを使っていますが、反対に任意の文字列(複数)と重複した行のみを残したいです。

既出でしたら申し訳ございません。
何卒よろしくお願いいたします。

Re: [626] 任意の文字列と重複した行だけを残したい

あすかぜ [2021/05/27 19:59:24]

削除の条件を反転させてみました。
こちらのマクロでどうでしょうか。
https://www.asukaze.net/soft/cassava/macro/asukaze/bbs626_20210527.cms

試してみてください。

Re: [626] 任意の文字列と重複した行だけを残したい

奈々氏 [2021/05/28 10:00:54]

早速のご対応ありがとうございます。
希望通りの動作が出来ました。
とても助かりました。

後出しで申し訳ないのですが、こちらと[617]のマクロは1列目を対象にしていますが、それぞれ「選択している列」を対象にすることは可能でしょうか?
何卒よろしくお願いいたします。

Re: [626] 任意の文字列と重複した行だけを残したい

大石剛司 [2021/05/28 14:02:10]

大石です。

今回も勉強のために考えてみましたが、質問があります。

「選択している列」とありますが、選択している列は1列でしょうか?それとも複数列でしょうか?

1列であれば「含まれる」が判定の基準になります。

複数列の場合は、「いずれかの列に含まれる」または「全ての列に含まれる」の判定が必要になります。

よろしくお願いします。

Re: [626] 任意の文字列と重複した行だけを残したい

奈々氏 [2021/05/28 14:28:39]

大石様

お世話になります。
選択している列は1列です。

今回、対象が3列目でしたので1列目に移動して作業をしたため、3列目を選択した状態でマクロが使えたら効率的と思いました。
よろしくお願いいたします。

Re: [626] 任意の文字列と重複した行だけを残したい

大石剛司 [2021/05/28 19:40:33]

大石です。

1列という前提で作成してみました。
判りやすいようにコメントを追加しています。


//選択範囲を記憶
bottom1 = SelBottom;
top1 = SelTop;
left1 = SelLeft;
right1 = SelRight;

//選択範囲の最上行が、先頭セルの位置と異なります
if (top1 != Top) {
 //メッセージ表示
 MessageBox("選択範囲の最上行が、先頭セルの位置と異なります");
 //中断
 return;
}

//選択範囲の最下行が、最終セルの位置と異なります
if (bottom1 != Bottom) {
 //メッセージ表示
 MessageBox("選択範囲の最下行が、最終セルの位置と異なります");
 //中断
 return;
}

//選択範囲の左桁と右桁が異なります
if (left1 != right1) {
 //メッセージ表示
 MessageBox("選択範囲の左桁と右桁が異なります");
 //中断
 return;
}

//インポート
import { arrayInputBox } from "lib/Array.cms";

//インプットボックス表示
array = arrayInputBox("抽出する文字列を改行区切りで入力してください。");

//選択範囲の最後から選択範囲の先頭まで
for (y = bottom1; y >= top1; y--) {
 //選択されている列に該当の文字列が無い場合は
 if (!array.includes([left1, y])) {
  //行を削除する
  DeleteRow(y);
 }
}

Re: [626] 任意の文字列と重複した行だけを残したい

奈々氏 [2021/05/31 17:59:11]

大石様

お返事が遅くなり申し訳ありません。
すぐに確認が出来ないのですが、後日試してみたいと思います。
どうもありがとうございました。

Re: [626] 任意の文字列と重複した行だけを残したい

いっち [2021/06/06 08:40:19]

お邪魔して申し訳ありません。

DeleteRow(y); を用いない方法で、マクロを書いてみました。
但し、このマクロの動作環境は、Ver.2.3 β4以降だと思います。
Ver.2.3 β6(2021/05/29)で、動作確認を行いました。
2.2.6 (2021/04/17)では、動きません。
割と大きめのcsvファイルでも、スムーズに動作する場合もあります。
※抽出されるデーターのボリュームによるようです。

// 任意の文字列(複数)と重複した行のみを残す。
import { arrayInputBox } from "lib/Array.cms";
path = GetFilePath();
if (path.length==0) {
 // 一度も保存されていないファイルの場合に保存するフォルダーの指定
 // Ver.2.3 β4(2021/04/17)
 // 実行時に出力先フォルダがない場合、自動的に作成
 path = "C:\\Cassava抽出Data\\";
}
fname = GetFileName();
if (fname.length==0) {
 fname = "抽出.csv";
}
save_date = GetYear()+GetMonth().padStart(2,"0")+GetDate().padStart(2,"0")+
"_"+GetHours().padStart(2,"0")+GetMinutes().padStart(2,"0")+
GetSeconds().padStart(2,"0")+"_";
// MessageBox(save_date);
// return;
fname = save_date + fname;
// 複数の列を選択した場合は、選択開始側の列になるようです。
col = x;
array = arrayInputBox("[" + col + "列]から抽出する文字列を改行区切りで入力してください。");
buffer = "";
flg = false;
for (y=1; y<=Bottom; y++) {
 if (array.includes([col,y])) {
  flg = true;
  for(x=1;x<=Right;x++) {
   if(x>1){
    buffer += ",";
   }
   cellstr = replace([x,y], "\"", "\"\"");
   // 今回の場合 pos(cellstr,"\n") は、抽出対象外かもしれません。
   if (pos(cellstr,",")||pos(cellstr,"\"")||pos(cellstr,"\n")) {
    cellstr = "\"" + cellstr + "\"";
   }
   buffer += cellstr;
  }
  buffer += "\n";
 }
}
if (!flg) {
 buffer += ("[" + col + "列]に見つかりません。\n");
}
WriteToFile(buffer, path + fname);
Open(path + fname);

Re: [626] 任意の文字列と重複した行だけを残したい

いっち [2021/06/07 08:22:12]

マクロの動作速度の比較をしてみました。

動作速度を比較したマクロ
────────────────────────────
1.あすかぜさんのマクロ bbs626_20210527.cms に対して、
if (!array.includes([1,y])) {

if (!array.includes([8,y])) {
に改めたもの

2,大石さんが [2021/05/28 19:40:33] に投稿したもの

3.私が [2021/06/06 08:40:19] に投稿したもの
────────────────────────────

比較したファイル
────────────────────────────
郵便局からダウンロードした
 13TOKYO.CSV
の文字コードを 「UTF-8のBOMなし」 に改めたもの
※シフト JISのままだと、半角カタカナを持ちているせいか、文字化けしやすいようです。

上記ファイルの「8列目」から、下記が一致するものを抽出

小笠原村
世田谷区
台東区
府中市
新宿区
稲城市

────────────────────────────

実行結果
────────────────────────────
1.あすかぜさんのマクロ bbs626_20210527.cms に対して、
if (!array.includes([1,y])) {

if (!array.includes([8,y])) {
に改めたもの
158秒~170秒位掛かりました。

2,大石さんが [2021/05/28 19:40:33] に投稿したもの
253秒~256秒掛かりました。
※動作速度より、画面の変化を楽しんでいるのかもしれんません(?)

3.私が [2021/06/06 08:40:19] に投稿したもの
5~6秒掛かりました。
────────────────────────────

比較方法
────────────────────────────
比較する各マクロに下記のコードを追加します。

import { toUnixTime } from "lib/UnixTime.cms";

// 計測を始める前の行に入れる。
start_time = toUnixTime(GetYear(), GetMonth(), GetDate(),GetHours(), GetMinutes(), GetSeconds());

// 計測を終える行の次に入れる。
end_time = toUnixTime(GetYear(), GetMonth(), GetDate(),GetHours(), GetMinutes(), GetSeconds());
exec_time = end_time - start_time;

// マクロの最終行に入れる。
MessageBox(exec_time);

※3.私が [2021/06/06 08:40:19] に投稿したもの の場合、
 Cassavaのオプション~ファイルのファイルを新しいウィンドウで開く
 にチェックを入れている場合は、MessageBox が新しく開いた抽出後のウィンドウの
 後ろに隠れていますので、タスクバーでアクティブなウィンドウを抽出する前の
 ファイルにする必要があります。
────────────────────────────

その他
────────────────────────────
上記と同様の事を、
郵便局からダウンロードした
 KEN_ALL.CSV
の文字コードを 「UTF-8のBOMなし」 に改めたもので行ったところ、
※3.私が [2021/06/06 08:40:19] に投稿したもの の場合、
14秒~15秒掛かりました。

1.あすかぜさんのマクロ bbs626_20210527.cms と
2,大石さんが [2021/05/28 19:40:33] に投稿したもの
では、KEN_ALL.CSVは試していません。
────────────────────────────
3.私が [2021/06/06 08:40:19] に投稿したものが、
動作が速いような事を書いてしまいましたが、
正式版 2.2.6 (2021/04/17)では動きません。

※あすかぜさんの「日記」をみると、正式版が出るのは、7月かもしれませんが、
こればかりは、推測に過ぎません。
待ち遠しいです。

Re: [626] 任意の文字列と重複した行だけを残したい

いっち [2021/06/07 08:57:21]

投稿してから気が付きました。
import { toUnixTime } from "lib/UnixTime.cms"; は、
「他のマクロから利用可能なライブラリ」の中の「UnixTime.cms」をダウンロードする必要がありました。

普段から利用させて頂いているライブラリなので、標準添付では無い事を忘れていました。

Re: [626] 任意の文字列と重複した行だけを残したい

大石剛司 [2021/06/07 19:59:34]

大石です。

速度の計測、ありがとうございます。
「DeleteRow(y);」を実行すると、毎回グリッド表示の再描画が実行されるので、行数が多い場合は処理時間が掛かっていると思われます。

StringGrid で表示している場合は、描画停止と描画再開のマクロ命令を追加してもらえれば、処理時間が短くなると思います。
Delphi の StringGrid では、自分のアプリケーションでは以下を使用しています。

//描画停止
SendMessage(StringGrid1.Handle, WM_SETREDRAW, 0, 0);

//処理

//描画再開
SendMessage(StringGrid1.Handle, WM_SETREDRAW, 1, 0);
StringGrid1.Repaint;

よろしくお願いします。

Re: [626] 任意の文字列と重複した行だけを残したい

あすかぜ [2021/06/07 20:11:52]

いっちさん、

いつもありがとうございます。
行の抽出はよく使われる処理なのに、DeleteRow() がかなり遅いのは問題だと認識しています。
素直に・簡潔に書いたマクロが十分速く動けばいいなぁと思うのですが、なかなか解決が難しく、DeleteRow() を使ったマクロは遅いままとなっています。
よりよい API や標準ライブラリについて、今後考えていきたいと思います。
Ver.2.3 では、WriteToFile() を使ってファイルに書き出してしまうのはだいぶ速くなりますね。

ちなみに、Ver.2.3 の正式リリースは推測のとおり 7 月を予定しています。
今のところこれ以上の機能追加は予定していないので、バグ報告や簡単に対応できる要望がなければ、β6 がそのまま正式版に昇格することになると思います。

Re: [626] 任意の文字列と重複した行だけを残したい

あすかぜ [2021/06/07 20:24:57]

大石さん、

ご提案ありがとうございます。
処理の実行中に描画を止めるのは、マクロ側の書き方を変えずに処理を高速化できるのでよいですね。
今後検討したいと思います。

・マクロの途中でダイアログの入力待ちにするときは最新の状態を表示したい
・処理に時間がかかっている場合、今どこまで進んでいるか確認したい
など、マクロの実行中でも画面を描画するほうが好ましい場合もあると思うので、どのようなタイミングで描画を抑制・再開すべきかも考えたいと思います。

Re: [626] 任意の文字列と重複した行だけを残したい

あすかぜ [2021/06/07 20:30:35]

すみません、大石さんの提案内容は「マクロの実行中は描画を止める」ではなく「描画の停止・再開を指示するマクロ命令を追加する」ですね。
マクロで明示的に停止・再開するのであれば画面が更新されることを期待するマクロの動作を壊さないので、安全に導入できますね。
導入を考えてみます。ありがとうございます。

Re: [626] 任意の文字列と重複した行だけを残したい

あすかぜ [2021/06/09 22:20:27]

別解として、Google Apps Script を参考にした Range API を標準ライブラリとして提供するということも考えています。


// https://www.asukaze.net/soft/cassava/macro/asukaze/bbs626_20210609/Array.cms
import { arrayInputBox } from "lib/Array.cms";
// https://www.asukaze.net/soft/cassava/macro/asukaze/bbs626_20210609/Range.cms
import { getDataRange } from "lib/Range.cms";

array = arrayInputBox("抽出する文字列を改行区切りで入力してください。");

range = getDataRange();
values = range.getValues().filter(row => array.includes(row[7]));
range.setValues(values);

if (values.length < Bottom) {
DeleteRow(values.length + 1, Bottom);
}


このマクロでも、メモリの消費量は多いですが、従来の書き方に比べて抽出処理を高速化できているのではないかと思います。


マクロを書くにあたって Cassava Editor 以外で一切役に立たないノウハウを覚えなければいけないのは申し訳ないので、Cassava Editor 2.0 以降ではできるだけマクロ言語を JavaScript に寄せる方向で拡張しています。
その方向で考えると、Cassava Editor 専用の描画停止・再開命令を提供するよりも、他のツールと同じ考え方で使える API を用意してそちらを推奨したほうがよいのかなぁ、という気もしています。
最近、Web 版の Excel でも同様に JavaScript でマクロが書けるようになったというニュースも見ましたし。

ただ、個人的には今までのように行番号で for 文をまわして直接テーブルを読み書きするほうがわかりやすいと思うので、悩ましいところです。

Re: [626] 任意の文字列と重複した行だけを残したい

いっち [2021/06/10 06:05:24]

お世話になります。

>従来の書き方に比べて抽出処理を高速化できているのではないかと思います。

動作確認を行いました。

1.13TOKYO.CSVの文字コードを 「UTF-8のBOMなし」 に改めたもの
3秒~4秒

2.KEN_ALL.CSVの文字コードを 「UTF-8のBOMなし」 に改めたもの
586秒~588秒

でした。

※KEN_ALL.CSV で動作察せる為、オプション~「動作の指定回数のループでマクロを強制終了する」を、
私は、200万回に改めました。
(これまでは100万回にしていたのですが)

>別解として、Google Apps Script を参考にした Range API を標準ライブラリとして提供するということも考えています。

そのようにして頂けるなら、便利になると思いました。

>Cassava Editor 2.0 以降ではできるだけマクロ言語を JavaScript に寄せる方向で拡張しています。

一般のテキストエディターでも、JavaScriptを利用するソフトは多く、JavaScript に寄せる方向の拡張は(とても)分かりやすいです。

>その方向で考えると、Cassava Editor 専用の描画停止・再開命令を提供するよりも、他のツールと同じ考え方で使える API を用意してそちらを推奨したほうがよいのかなぁ、という気もしています。

私が [2021/06/06 08:40:19] に投稿した WriteToFile() を用いた方法は、
Cassavaが従来から持っている「エクスポートマクロ」をWriteToFile()に置き換えただけですから、
write()やwriteln()に戻して「エクスポートマクロ」にすればさらに動作が速くなり、

1.13TOKYO.CSVの文字コードを 「UTF-8のBOMなし」 に改めたもの
2秒(arrayInputBoxでOKを押した後の時間です)

2.KEN_ALL.CSVの文字コードを 「UTF-8のBOMなし」 に改めたもの
10秒~11秒(arrayInputBoxでOKを押した後の時間です)

になりました。

Cassavaは、「エクスポートマクロ」という素晴らしい機能を、既に持っているのですから、
>Cassava Editor 専用の描画停止・再開命令
に、必ずしもこだわる必要はないと(私は)思いました。

※KEN_ALL.CSV 位のファイルになると、「描画停止・再開」に関わらず、DeleteRow()の処理時間がそのものが影響しそうです。
(13TOKYO.CSV 位なら、「描画停止・再開」で確かに速くなりそうです。)

↑実は私は、AutoItでマクロ実行中Cassavaを消す(簡単な)スクリプトを作りました。

>最近、Web 版の Excel でも同様に JavaScript でマクロが書けるようになったというニュースも見ましたし。
スプレッドシートなら、JavaScriptでいくつかのマクロを書いてみましたが、Excelでも出来るようになったのは知りませんでした。

>ただ、個人的には今までのように行番号で for 文をまわして直接テーブルを読み書きするほうがわかりやすいと思うので、悩ましいところです。
今までの方法が一番分かりやすいので、利用するファイルの大きさでマクロの組み方を分ければ良いだけのように(私は)感じます。

Re: [626] 任意の文字列と重複した行だけを残したい

いっち [2021/06/12 08:35:37]

お世話になります。
あすかぜさんが書かれた
>あすかぜ [2021/06/09 22:20:27]
>別解として、Google Apps Script を参考にした Range API を標準ライブラリとして提供するということも考えています。
を見ていて、どうしてこんなに速いのか、ずっと考えていました。

まだ何も入力されていない画面に文字を入力する場合には、予めセルを広げておけば良いだけでした。
下記は、動作確認(だけなら)使えるかも(?)と思いました。

// まだ何も入力されていない画面に、文字を入力する。
import { toUnixTime } from "lib/UnixTime.cms";
obj_str = {};
obj_str[0] = {1:"あいうえお",2:"アイウエオ"};
obj_str[1] = {1:"かきくけこ",2:"カキクケコ"};
obj_str[2] = {1:"さしすせそ",2:"サシスセソ"};
obj_str[3] = {1:"たちつてと",2:"タチツテト"};
obj_str[4] = {1:"なにぬねの",2:"ナニヌネノ"};
obj_str[5] = {1:"はひふへほ",2:"ハヒフヘホ"};
obj_str[6] = {1:"まみむめも",2:"マミムメモ"};
obj_str[7] = {1:"やゆよ",2:"ヤユヨ"};
obj_str[8] = {1:"らりるれろ",2:"ラリルレロ"};
obj_str[9] = {1:"わをん",2:"ワヲン"};
start_time = toUnixTime(GetYear(), GetMonth(), GetDate(),GetHours(), GetMinutes(), GetSeconds());

// 予め入力セルを広げておくと、動作が速くなる。
b_num = 10000;
r_num = 2;
if (Bottom<b_num) Bottom = b_num;
if (Right<r_num) Right = r_num;

for (y=0; y<b_num; y++) {
 for (x=1;x<=r_num;x++) {
  [x,y+1] = obj_str[y%10][x];
 }
}
// Refresh();
end_time = toUnixTime(GetYear(), GetMonth(), GetDate(),GetHours(), GetMinutes(), GetSeconds());
exec_time = end_time - start_time;
MessageBox(exec_time);

Re: [626] 任意の文字列と重複した行だけを残したい

あすかぜ [2021/06/13 11:51:46]

はい、Cassava Editor ではグリッドのサイズ変更にかなり時間がかかってしまうので、1行ずつ挿入や削除をするのではなく複数行をまとめて処理することで高速化できます。

Re: [626] 任意の文字列と重複した行だけを残したい

あすかぜ [2021/07/11 08:11:05]

DeleteRow() を高速化した 2.4 α1 を公開しました。
https://www.asukaze.net/soft/cassava/history/2_4dev.html

マクロ処理の中で DeleteRow() をすぐに実行せず、隣り合った行の削除命令が来たら自動的に処理をまとめるように変更してみました。
bbs626_20210527.cms のような、高速化を意識せずに書いたマクロでも十分な速度が出るようになったのではないかと思います。

不具合等あればご指摘いただけると助かります。
よろしくお願いします。

Re: [626] 任意の文字列と重複した行だけを残したい

いっち [2021/07/12 07:45:22]

お世話になります。

> bbs626_20210527.cms のような、高速化を意識せずに書いたマクロでも十分な速度が出るようになったのではないかと思います。

「KEN_ALL.CSV」で確認しました。

これまで動作確認を行っていたパソコンでは、67秒位掛かるようですが、
最近購入し利用し始めたパソコンでは、24秒位でした。

とても利用しやすくなりました。

> 不具合等あればご指摘いただけると助かります。

分かりました。

Re: [626] 任意の文字列と重複した行だけを残したい

いっち [2021/07/28 23:35:29]

>DeleteRow() を高速化した 2.4 α1 を公開しました。

データーが(ソートされたように)きれいに並んでいる時に(特に)高い効果があって、空白行をまとめて削除する場合などの場合も快適です。
マクロでがんばって処理をまとめるコードを書くより、Cassavaが内部的に持っているこの機能を利用する方が動作が速いような気がしています。

Re: [626] 任意の文字列と重複した行だけを残したい

あすかぜ [2021/07/29 20:39:25]

動作確認ありがとうございます。
期待通りに高速化できているようでよかったです。

スレッド一覧に戻る返信