Yahoo!地図情報Flash版のルート再生機能を使用する際に必要となるルート情報をxmlファイルで生成する際に、全部の軌跡を緯度・経度で取得するのが面倒なので、経由地点を地図で指定していくと、自動的にその途中の緯度・経度を計算して作成し、xmlを作成するプログラムを作ってみました。
こちらからどうぞ(このページの動作のみ、Win2000,IEで確認)
Yahoo!地図情報Flash版のルート再生機能を使用する際に必要となるルート情報をxmlファイルで生成する際に、全部の軌跡を緯度・経度で取得するのが面倒なので、経由地点を地図で指定していくと、自動的にその途中の緯度・経度を計算して作成し、xmlを作成するプログラムを作ってみました。
こちらからどうぞ(このページの動作のみ、Win2000,IEで確認)
この記事は、複数の列を比べて重複行を探す、というものです。
「単独の列で重複している行を探す」というサンプルは、
VBAのお勉強 VBAでExcelの重複した行を探して表示
をご覧下さい。
「重複している行を1行だけ残して削除する」というサンプルは、
VBAのお勉強 Excelで1行残して重複行を削除
をご覧下さい。
http://q.hatena.ne.jp/1205154937
「列単位でコンペアさせ、コンペア結果、重複データがある場合」という条件がはっきりしないので、
勝手に「Sheet1のA列のある行のデータがSheet2のA列のどこかにあった場合、重複データとする」としてみました。
イメージとしては、
- あったらその値をSheet3に書き出す、なければそのまま。
という感じでしょうか。
例えばこんな感じのワークシートがあったとします。


まずSheet1のA列の1行目の値「a」に注目します。
この値がSheet2のA列の1~7行目にあれば、Sheet3に書き出します。
この場合、「a」はありませんので何もしません。
次、Sheet1の2行目の「b」。これもないので何もしません。
次、Sheet1の3行目の「c」。これはSheet2のA列の1行目にありますので、 Sheet3に書き出します。
書き出す位置は、これが見つかった値の1個めですからA1にします。
次、Sheet1の4行目の「1」。これもないので何もしません。
次、Sheet1の5行目の「2」。これもないので何もしません。
次、Sheet1の6行目の「3」。これはSheet2のA列の4行目にありますので、 Sheet3に書き出します。
書き出す位置は、これが見つかった値の2個めですからA2にします。
これでSheet1の全部の行を見ました。
これでおっけい(^^)
ではこれをプログラムにしてみます。
Excelから「ツール」→「マクロ」→「Visual Basic Editor」でプロジェクトの中の「Sheet1」をダブルクリック、するとコードを記述するウィンドウが表示されます。
まず、Sheet1のA列を一行ずつ見ていく必要があるので、とりあえず、イミディエイトウィンドウに Sheet1のA列を1行ずつ表示するプログラムを作ってみます。
この記事でやったことと全く同じですね(^^;)
Sub test()
Dim lastgyou As Integer
Dim i As Integer
ActiveSheet.Range("A1").End(xlDown).Select
lastgyou = ActiveCell.Row
For i = 1 To lastgyou
Debug.Print (ActiveSheet.Cells(i, 1).Value)
Next
End Sub
で、実行してみます。
ん、いいんでないかい(^^)
次、こうやって取り出した値を1つずつ、Sheet2にあるかどうかを探す事になりますので、 Sheet2も同じように、A列の何行目までデータが入っているかを調べてみます。
。。。はて。上のプログラムだと、ActiveSheetってなってますので、今現在選択しているシートの事しかわからないですね。
というか、これじゃ、このプログラムを実行したときに選択されているシートの値を表示しちゃうかな?
試しに、Sheet2を選択した状態でこのプログラムを動かしてみると、
(^^;)Sheet2の値が表示されちゃいました。
これじゃ駄目ですね。 どうしたもんか。。。
どのシートが選択されている状態でもSheet1の値を見るようにしないとまずいですね。
プログラムでシートを選択する方法を調べてみます。
Yahoo!で「vba excel シートの選択」で検索すると、こちらのページが見つかりました。
Sheets(“Sheet1″).Select で良いみたいですね。 これをプログラムの先頭に入れてみます。
Sub test()
Dim lastgyou As Integer
Dim i As Integer
Sheets("Sheet1").Select
ActiveSheet.Range("A1").End(xlDown).Select
lastgyou = ActiveCell.Row
For i = 1 To lastgyou
Debug.Print (ActiveSheet.Cells(i, 1).Value)
Next
End Sub
Sheet2を選択した状態で、
プログラムを実行してみます。
ん、Sheet1に切り替わってますね。いいかんじ(^^)
Sheet2について調べたければ Sheets(“Sheet2″).select に切り替えればいいですね。
それでは、Sheet1とSheet2、それぞれ何行目までデータが入っているかを調べてみます。
Sub test()
Dim sheet1lastgyou As Integer
Dim sheet2lastgyou As Integer
Sheets("Sheet1").Select
ActiveSheet.Range("A1").End(xlDown).Select
sheet1lastgyou = ActiveCell.Row
Sheets("Sheet2").Select
ActiveSheet.Range("A1").End(xlDown).Select
sheet2lastgyou = ActiveCell.Row
Debug.Print ("sheet1は" + Str(sheet1lastgyou) + "行目まで")
Debug.Print ("sheet2は" + Str(sheet2lastgyou) + "行目まで")
End Sub
Sheet1の最終行の行番号がsheet1lastgyouに入り、Sheet2がsheet2lastgyouです。

ん、よいようです(^^)
それではとりあえず、Sheet1のデータがSheet2にあるかどうかを調べるプログラムを作ってみます。
Sub test()
Dim i As Integer
Dim j As Integer
Dim check As String
Dim sheet1lastgyou As Integer
Dim sheet2lastgyou As Integer
'Sheet1の最終行を求める
Sheets("Sheet1").Select
ActiveSheet.Range("A1").End(xlDown).Select
sheet1lastgyou = ActiveCell.Row
'Sheet2の最終行を求める
Sheets("Sheet2").Select
ActiveSheet.Range("A1").End(xlDown).Select
sheet2lastgyou = ActiveCell.Row
'Sheet1を1行目から最終行まで
For i = 1 To sheet1lastgyou
'Sheet1を選択します
Sheets("Sheet1").Select
'Sheet1のi行目の値を取り出します
check = ActiveSheet.Cells(i, 1).Value
'Sheet2を選択します
Sheets("Sheet2").Select
'Sheet2を1行目から最終行まで
For j = 1 To sheet2lastgyou
'Sheet2のj行目の値とcheckの値が同じだったら
If ActiveSheet.Cells(j, 1).Value = check Then
'とりあえずイミディエイトウィンドウに表示します
Debug.Print (check)
End If
Next
Next
End Sub
プログラムにコメントを入れてみました。やってることは、さっき言葉で書いたことそのままです。
動かしてみます。

ん、重複している値(「c」と「3」)が表示されました(^^)
あとは、この見つけた値をSheet3に書き出していけばいいですね。
1つ目を見つけたら、Sheet3のA1にその値を入れる、
2つ目を見つけたら、Sheet3のA2にその値を入れる、
以下繰り返しって感じで。
今何個目なのかを入れておく変数を作り、見つけたら、セルに値を入れ、変数を1つ増やす、これで行きます。
Sub test()
Dim i As Integer
Dim j As Integer
Dim check As String
Dim sheet1lastgyou As Integer
Dim sheet2lastgyou As Integer
Dim sheet3gyou As Integer
'Sheet1の最終行を求める
Sheets("Sheet1").Select
ActiveSheet.Range("A1").End(xlDown).Select
sheet1lastgyou = ActiveCell.Row
'Sheet2の最終行を求める
Sheets("Sheet2").Select
ActiveSheet.Range("A1").End(xlDown).Select
sheet2lastgyou = ActiveCell.Row
'Sheet3の行数を0にしておく
sheet3gyou = 0
'Sheet1を1行目から最終行まで
For i = 1 To sheet1lastgyou
'Sheet1を選択します
Sheets("Sheet1").Select
'Sheet1のi行目の値を取り出します
check = ActiveSheet.Cells(i, 1).Value
'Sheet2を1行目から最終行まで
For j = 1 To sheet2lastgyou
'Sheet2を選択します
Sheets("Sheet2").Select
'Sheet2のj行目の値とcheckの値が同じだったら
If ActiveSheet.Cells(j, 1).Value = check Then
'Sheet3に代入するセルの行数を1つ増やします。
sheet3gyou = sheet3gyou + 1
'Sheet3を選択します
Sheets("Sheet3").Select
'セルに見つけた値を代入します
ActiveSheet.Cells(sheet3gyou, 1).Value = check
End If
Next
Next
End Sub
sheet3gyouという変数を設け、最初は0、見つけたら1つ増やして、 Sheet3に切り替え、見つけた値をセルに代入する、という感じです。
プログラムを動かしてみると。。。

ん、いいんでないかい?(^^)
これで完成、、、と思ったんですけど。
今入ってるデータだったら問題ないんですけど、 Sheet1内、Sheet2内で重複しているデータがあった場合、例えばこんな感じ。

この状態で実行すると、

こんな感じ。「a」はいいとして、「b」「c」が2つ出てます。
「b」はSheet1に2つありますから、それぞれがSheet2の1つと一致します。
なので、2つ出ます。「c」はSheet1の1つがSheet2の2つと一致しますので、やはりこれも2つ出ます。
重複しているものは出ているので、これでいいっちゃいいんですけど、ここは、

こう出たほうがスマートですかね?(^^;)
さて、どうやろうかな。
Sheet2の中で重複している場合(この場合「c」)は、Sheet2を上から見ていくとき、 1つ目が見つかった段階で、もうその値を探すのを止めてしまえばよさそうです。
見つかった場合、forのループを抜け出してしまう感じですね。
Sub test()
Dim i As Integer
Dim j As Integer
Dim check As String
Dim sheet1lastgyou As Integer
Dim sheet2lastgyou As Integer
Dim sheet3gyou As Integer
'Sheet1の最終行を求める
Sheets("Sheet1").Select
ActiveSheet.Range("A1").End(xlDown).Select
sheet1lastgyou = ActiveCell.Row
'Sheet2の最終行を求める
Sheets("Sheet2").Select
ActiveSheet.Range("A1").End(xlDown).Select
sheet2lastgyou = ActiveCell.Row
'Sheet3の行数を0にしておく
sheet3gyou = 0
'Sheet1を1行目から最終行まで
For i = 1 To sheet1lastgyou
'Sheet1を選択します
Sheets("Sheet1").Select
'Sheet1のi行目の値を取り出します
check = ActiveSheet.Cells(i, 1).Value
'Sheet2を1行目から最終行まで
For j = 1 To sheet2lastgyou
'Sheet2を選択します
Sheets("Sheet2").Select
'Sheet2のj行目の値とcheckの値が同じだったら
If ActiveSheet.Cells(j, 1).Value = check Then
'Sheet3に代入するセルの行数を1つ増やします。
sheet3gyou = sheet3gyou + 1
'Sheet3を選択します
Sheets("Sheet3").Select
'セルに見つけた値を代入します
ActiveSheet.Cells(sheet3gyou, 1).Value = check
'1つ目が見つかったので、ループから抜けます
Exit For
End If
Next
Next
End Sub
見つかった後、セルに代入した後でExit forでループを抜け出します。実行してみると、

ん、「c」は1つだけになりました(^^)。
後は、Sheet1内で重複していた場合ですね。
Sheet1を1行ずつ見ていって、これからチェックしようとする値(checkという変数に代入しています)が、それまで見てきた行の中にあるかどうかをチェックし、もしもあった場合、その行を飛ばす、というのでどうでしょうか。
これなら、Sheet1の3行目の「b」を見ているときは、すでに2行目に「b」がありますから、飛ばすことになります。
というわけで、プログラムにしてみます。
Sub test()
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim atta As Integer
Dim check As String
Dim sheet1lastgyou As Integer
Dim sheet2lastgyou As Integer
Dim sheet3gyou As Integer
'Sheet1の最終行を求める
Sheets("Sheet1").Select
ActiveSheet.Range("A1").End(xlDown).Select
sheet1lastgyou = ActiveCell.Row
'Sheet2の最終行を求める
Sheets("Sheet2").Select
ActiveSheet.Range("A1").End(xlDown).Select
sheet2lastgyou = ActiveCell.Row
'Sheet3の行数を0にしておく
sheet3gyou = 0
'Sheet1を1行目から最終行まで
For i = 1 To sheet1lastgyou
'Sheet1を選択します
Sheets("Sheet1").Select
'Sheet1のi行目の値を取り出します
check = ActiveSheet.Cells(i, 1).Value
'attaという変数を0にします。
atta = 0
'もし、Sheet1の2行目以降を見ている場合
If i >= 2 Then
'今見ている行の前の行までに今回の値があるかどうかを探します
For k = 1 To i - 1
If ActiveSheet.Cells(k, 1).Value = check Then
atta = 1
Exit For
End If
Next
End If
'もし、atta=0ならば、Sheet2を探します。
If atta = 0 Then
'Sheet2を1行目から最終行まで
For j = 1 To sheet2lastgyou
'Sheet2を選択します
Sheets("Sheet2").Select
'Sheet2のj行目の値とcheckの値が同じだったら
If ActiveSheet.Cells(j, 1).Value = check Then
'Sheet3に代入するセルの行数を1つ増やします。
sheet3gyou = sheet3gyou + 1
'Sheet3を選択します
Sheets("Sheet3").Select
'セルに見つけた値を代入します
ActiveSheet.Cells(sheet3gyou, 1).Value = check
'1つ目が見つかったので、ループから抜けます
Exit For
End If
Next
End If
Next
End Sub
新たに、attaとkという変数を作りました。
もし、Sheet1の2行目以降を探す場合、それ以前の行の値を For k=1 to i-1 ということで、1行目から、今探している行(i)の1つ前までループし、その行のセルの値が、checkの値と同じかどうかを調べ、同じだったら、attaという変数に1を入れます。
もし見つからなかったら、attaの変数は0のままですので、 attaが0だったら、今度はSheet2を調べる、という事になります。
プログラムを実行すると、、、

ん、いいんでないかい(^^)
いちおう、これで完成、でいいかな・・・?
こちらにアップしておきます→vbastudy_0006.xls
前の記事の仕組みを使って、ちょっとお遊びで作りました(^^;)Yahoo!のトップページのトピックスから見出しを取ってきて、適当につぎはぎにして表示するプログラムです。
まったく乱数で表示してるので、出てくる見出しはでたらめです。
取ったテキストを抜き出してなんやらかんやら加工をかけてます。
もっとスマートな方法があるとは思うんですが、、、今の自分では
この辺で限界(^^;)
JavaScriptで正規表現とか、配列の操作、タイマー、乱数とかほとんどはじめて使ってみました。今後こういったプログラムを書くときのために残しておきます。
やはりこれもローカルでないと動きません。
<?xml version="1.0">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script language="javascript">
<!--
varhttpObj;
function httpRequest(target_url)
{
try
{
httpObj = false;
if(window.XMLHttpRequest) {
// Firefox, Opera など
httpObj = new XMLHttpRequest();
} else if(window.ActiveXObject) {
// IE
try {
httpObj = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
httpObj = new ActiveXObject('Microsoft.XMLHTTP');
}
}
}
catch(e)
{
alert('エラーです');
return;
}
//データを取得する
httpObj.open("GET", target_url, true);
httpObj.onreadystatechange = DataRead;
httpObj.send("");
return;
}
function DataRead()
{
if (httpObj.readyState == 4 && httpObj.status == 200)
{
//取得したレスポンスをgetTextに入れる
var getText=httpObj.responseText;
//getText1にgetTextの「<ul class="emphasis">」から始まるテキストを入れる
var getText1=getText.substring(getText.indexOf('<ul class="emphasis">'));
//getText2にgetText1の「</ul>」の前までのテキストを入れる
var getText2=getText1.substring(0,getText1.indexOf('</ul>'));
//getText3の配列に、<a>~</a>を入れる
var getText3=getText2.match(/<a.+?>.+?</a>/g);
//すべてのgetText3の配列のタグを取り除く
for (var i=0;i<getText3.length;i++){
var xx=getText3[i].match(/<a.+?>(.+?)</a>/);
getText3[i]=RegExp.$1;
}
//すべてのgetText3の「、」を半角スペースに置き換え
for (var i=0;i<getText3.length;i++){
getText3[i]=getText3[i].replace('、',' ').replace('「',' ').replace('」',' ');
}
//すべてのgetText3の連続する半角スペースを1つにする
for (var i=0;i<getText3.length;i++){
while (getText3[i].indexOf(' ')!=-1){
getText3[i]=getText3[i].replace(' ',' ');
}
}
//すべてのgetText3の両端の半角スペースを削除する
for (var i=0;i<getText3.length;i++){
getText3[i]=getText3[i].replace(/^[ ]*/gim, "").replace(/[ ]*$/gim,"");
}
//半角スペースがあるものだけがつぎはぎの対象なので、それの要素番号をtaishouYousoにpushする
var taishouYouso=new Array();
for (var i=0;i<getText3.length;i++){
if (getText3[i].indexOf(' ')!=-1){
taishouYouso.push(i);
}
}
//もし要素が1つ以下の場合、作れないのでごめんなさい
if (taishouYouso.length<=1){
alert('今の見出しでは作れませんでした。。。ごめんなさい');
} else {
//異なる2つの要素を取得する
ret=" ";
for (var youso1=0;youso1<taishouYouso.length;youso1++){
do {
var youso2=Math.floor( Math.random() * taishouYouso.length );
} while (youso1==youso2)
//1つめの要素の前半部分と、2つめの要素の後半部分をくっつけて見出しを作る
var nukidashi1=getText3[taishouYouso[youso1]].substr(0,getText3[taishouYouso[youso1]].indexOf(' '));
var nukidashi2=getText3[taishouYouso[youso2]].substr(getText3[taishouYouso[youso2]].indexOf(' '));
ret=ret+nukidashi1+nukidashi2+' ';
}
setTimeout("scroll()",200);
}
}
}
function getTsugihagi(){
httpRequest('http://www.yahoo.co.jp/');
}
function scroll(){
ret=ret.substring(1,ret.length)+ret.substring(0,1);
document.fm.txt.value=ret;
setTimeout("scroll()",200);
}
//-->
</script>
</head>
<body onload="getTsugihagi()">
<form name="fm">
<input type="text" name="txt" size="30">
</form>
</body>
</html>
問い合わせ先をこのサーバー上のCGI(Yahoo!からトップのソースを取ってくるだけ)にして、動作するものを置いておきます。こちらからどぞ。
http://q.hatena.ne.jp/1204789749
この質問で「スクレイピング」ってはじめて知りました。
同様のことは他のツールを使ってやってましたけど、
JavaScriptだけでこんなことができるなんて、すごい(^^)
回答者さんのリンク先のプログラムを参考に、任意の
銘柄コードを入力すると、Yahoo!からデータを取ってくる
プログラムを作ってみました。
<?xml version="1.0">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script language="javascript" type="text/javascript">
<!--
varhttpObj;
function httpRequest(target_url)
{
try
{
if(window.XMLHttpRequest)
{
httpObj = new XMLHttpRequest();
}
else if(window.ActiveXObject)
{
httpObj = new ActiveXObject("Microsoft.XMLHTTP");
}
else
{
alert('エラーです');
return;
}
}
catch(e)
{
alert('エラーです');
return;
}
//データを取得する
httpObj.open("GET", target_url, true);
httpObj.onreadystatechange = DataRead;
httpObj.send("");
return;
}
function DataRead()
{
if (httpObj.readyState == 4 && httpObj.status == 200)
{
//取得したレスポンスをgetTextに入れる
var getText=httpObj.responseText;
//kanrenにgetTextの「関連情報」から始まるテキストを入れる
var kanren=getText.substring(getText.indexOf('関連情報',0));
//trtagにkanrenの「<tr」から始まるテキストを入れる
var trtag=kanren.substring(kanren.indexOf('<tr',0));
//trtagの5つ目の「<td」の位置を調べて
var found=-1;
for (var i=1;i<=5;i++){
found=trtag.indexOf('<td',found+1);
}
//trtagの5つ目の「<td」以降の文字列で「<b」で始まるところの3文字あとの文字列をstartkabukaに入れる
var startkabuka=trtag.substring(trtag.indexOf('<b',found)+3);
//startkabukaの「</b」で始まる前までの文字をkabukaに入れる
var kabuka=startkabuka.substring(0,startkabuka.indexOf('</b',0));
//kabukaをフォームに入れる
document.form1.getkabuka.value=kabuka;
}
}
//-->
</script>
</head>
<body>
<form name="form1">
銘柄コード<input type="text" name="code" style="ime-mode:disabled">の
<input type="button" value="株価GET" onclick="httpRequest('http://quote.yahoo.co.jp/q?s='+document.form1.code.value+'&d=v1&k=c3&h=on&z=m')"><br>
株価<input type="text" name="getkabuka">円
</form>
</body>
</html>
取得した文字列から必要なところを抜き出すのは、きっと
もっと良い方法があるんでしょうけど、とりあえずベタな
文字列関数で。
こちらにおいておきます→
http://www.simple-sys.com/archive/jsstudy_4.html
と思ってサーバーに置いて実行しようとしたら、エラーが出ちゃいました(^^;)
多分、Same-Originポリシー
によるものかな?
ローカルにHTMLファイルを落とせば使えました。
あとブラウザーによって使えなかったりするのかな?
IE6なら動きました。
こちらによれば、
new ActiveXObject(“Microsoft.XMLHTTP”) はIEでしか使えないっぽいですね。
http://q.hatena.ne.jp/1204273508
とにかく簡単に。
<html>
<head>
<script>
function keisan(){
if (document.fm.tate.value=='50'){
//縦が「50」の場合
if (document.fm.yoko.value=='10'){
kekka=500;
} else
if (document.fm.yoko.value=='20'){
kekka=1000;
} else
if (document.fm.yoko.value=='30'){
kekka=1500;
}
}
if (document.fm.tate.value=='100'){
//縦が「100」の場合
if (document.fm.yoko.value=='10'){
kekka=1000;
} else
if (document.fm.yoko.value=='20'){
kekka=2000;
} else
if (document.fm.yoko.value=='30'){
kekka=3000;
}
}
if (document.fm.tate.value=='200'){
//縦が「200」の場合
if (document.fm.yoko.value=='10'){
kekka=2000;
} else
if (document.fm.yoko.value=='20'){
kekka=4000;
} else
if (document.fm.yoko.value=='30'){
kekka=6000;
}
}
//値段に kekka を入れる
document.fm.nedan.value=kekka;
}
</script>
</head>
<body onLoad="keisan()">
<form name="fm">
縦
<select name="tate" onChange="keisan()">
<option value="50">50
<option value="100">100
<option value="200">200
</select>×
横
<select name="yoko" onChange="keisan()">
<option value="10">10
<option value="20">20
<option value="30">30
</select><br>
値段は<input type="text" name="nedan">円
</form>
</body>
</html>
上のソースを
http://www.simple-sys.com/archive/jsstudy_3.html
に置いておきました。
http://q.hatena.ne.jp/1202199353
Checkの部分が大分長くなっちゃったけど。
(Q1が1もしくは2もしくは3の場合)でかつ、
(Q2が選択されてない または Q3が選択されてない)場合アラート
って感じで。
<html>
<head>
<SCRIPT Language="JavaScript">
<!--
function Check()
{
if ((document.test.Q01[0].checked==true || document.test.Q01[1].checked==true || document.test.Q01[2].checked==true) && (document.test.Q02[0].checked==false && document.test.Q02[1].checked==false && document.test.Q02[2].checked==false && document.test.Q02[3].checked==false || document.test.Q03[0].checked==false && document.test.Q03[1].checked==false && document.test.Q03[2].checked==false && document.test.Q03[3].checked==false && document.test.Q03[4].checked==false)) { alert("問1で「1~3」と回答した人は、問2と問3にもお答えください"); return false; }
}
// -->
</SCRIPT>
</head>
<body>
<form name="test" onsubmit="return Check();">
問1<br>
<input type="radio" name="Q01" value="1">1<br>
<input type="radio" name="Q01" value="2">2<br>
<input type="radio" name="Q01" value="3">3<br>
<input type="radio" name="Q01" value="4">4<br>
<br><br>
問1で「1~3」を選んだ方は、問2、問3もお答えください<br><br>
問2<br>
<input type="radio" name="Q02" value="1">1<br>
<input type="radio" name="Q02" value="2">2<br>
<input type="radio" name="Q02" value="3">3<br>
<input type="radio" name="Q02" value="4">4<br>
<br>
問3<br>
<input type="radio" name="Q03" value="1">1<br>
<input type="radio" name="Q03" value="2">2<br>
<input type="radio" name="Q03" value="3">3<br>
<input type="radio" name="Q03" value="4">4<br>
<input type="radio" name="Q03" value="5">5<br>
<br>
<input type="submit" value="submit">
</form>
</body>
</html>
上記のソースを設置したのが、こちら→http://www.simple-sys.com/archive/jsstudy_2.html
http://q.hatena.ne.jp/1202193955
ラジオボタンがチェックされてるかどうかをチェックするのに、
radioの名前の後ろに [ ] で数字を指定してできるんですね。
勉強になりました。
<html>
<head>
<SCRIPT Language="JavaScript">
<!--
function Check()
{
if (document.test.Q01[3].checked==true && document.test.Q02[0].checked==false && document.test.Q02[1].checked==false && document.test.Q02[2].checked==false && document.test.Q02[3].checked==false) { alert("問1で「持っていない」と回答した人は、問2にもお答えください"); return false; }
}
// -->
</SCRIPT>
</head>
<body>
<form name="test" onsubmit="return Check();">
問1<br>
<input type="radio" name="Q01" value="1">1<br>
<input type="radio" name="Q01" value="2">2<br>
<input type="radio" name="Q01" value="3">3<br>
<input type="radio" name="Q01" value="4">持ってない<br>
<br><br>
問2<br>
<input type="radio" name="Q02" value="1">1<br>
<input type="radio" name="Q02" value="2">2<br>
<input type="radio" name="Q02" value="3">3<br>
<input type="radio" name="Q02" value="4">4<br>
<br>
<input type="submit" value="submit">
</form>
</body>
</html>
上記のソースを設置したのが、こちら→http://www.simple-sys.com/archive/jsstudy_1.html
前の記事の
回答者さんのを見て、order by について調べると、
列の番号で指定したり、order by に計算式を設定したりも出来るらしい。
まず、列の番号で指定する方法。
mysql> select A,sum(C)/sum(B)*100 from hoge group by A order by 2 desc; +--------+-------------------+ | A | sum(C)/sum(B)*100 | +--------+-------------------+ | 木村 | 154.54545454545 | | 伊藤 | 109.67741935484 | | 山田 | 40 | +--------+-------------------+ 3 rows in set (0.00 sec)
これで列に名前を付けなくても、並べ替えられた。
次に、order by に計算式を設定する場合。
mysql> select A,concat(round(sum(C)/sum(B)*100,1),"%") as RIEKIRITU from hoge group by A o rder by sum(C)/sum(B) desc; +--------+-----------+ | A | RIEKIRITU | +--------+-----------+ | 木村 | 154.5% | | 伊藤 | 109.7% | | 山田 | 40.0% | +--------+-----------+ 3 rows in set (0.00 sec)
ただ、これ件数が多くなると2回計算される形になるから遅くなったりするのかな?
http://q.hatena.ne.jp/1199118290
とりあえず、下のような表で。
mysql> select * from hoge; +--------+-----+-----+ | A | B | C | +--------+-----+-----+ | 木村 | 100 | 200 | | 山田 | 100 | 0 | | 伊藤 | 200 | 540 | | 木村 | 120 | 140 | | 山田 | 500 | 240 | | 伊藤 | 420 | 140 | +--------+-----+-----+ 6 rows in set (0.00 sec)
まず、グループ化するには、group by
mysql> select A from hoge group by A; +--------+ | A | +--------+ | 伊藤 | | 山田 | | 木村 | +--------+ 3 rows in set (0.00 sec)
グループ毎に合計するには、sum
mysql> select A,sum(B),sum(C) from hoge group by A; +--------+--------+--------+ | A | sum(B) | sum(C) | +--------+--------+--------+ | 伊藤 | 620 | 680 | | 山田 | 600 | 240 | | 木村 | 220 | 340 | +--------+--------+--------+ 3 rows in set (0.00 sec)
利益率を求める
mysql> select A,sum(C)/sum(B)*100 from hoge group by A; +--------+-------------------+ | A | sum(C)/sum(B)*100 | +--------+-------------------+ | 伊藤 | 109.67741935484 | | 山田 | 40 | | 木村 | 154.54545454545 | +--------+-------------------+ 3 rows in set (0.00 sec)
利益率の項目に項目名をつける。asを使う
mysql> select A,sum(C)/sum(B)*100 as RIEKIRITU from hoge group by A; +--------+-----------------+ | A | RIEKIRITU | +--------+-----------------+ | 伊藤 | 109.67741935484 | | 山田 | 40 | | 木村 | 154.54545454545 | +--------+-----------------+ 3 rows in set (0.00 sec)
利益率の項目が大きい順に並べ替える。order by
mysql> select A,sum(C)/sum(B)*100 as RIEKIRITU from hoge group by A order by RIEKIRITU desc; +--------+-----------------+ | A | RIEKIRITU | +--------+-----------------+ | 木村 | 154.54545454545 | | 伊藤 | 109.67741935484 | | 山田 | 40 | +--------+-----------------+ 3 rows in set (0.00 sec)
利益率を小数第一位で四捨五入する。round
mysql> select A,round(sum(C)/sum(B)*100,1) as RIEKIRITU from hoge group by A order by RIEKIRITU desc; +--------+-----------+ | A | RIEKIRITU | +--------+-----------+ | 木村 | 154.5 | | 伊藤 | 109.7 | | 山田 | 40.0 | +--------+-----------+ 3 rows in set (0.00 sec)
とりあえずこれで質問者さんが必要としてるデータはでたと思う。
不明点が何個か。
order by RIEKIRITUで、利益率の項目でas で名前を付けたのを使ったけど、これは名前を付けないとorder by で指定できないのだろうか?
あと、質問者さんの求める形は、利益率に%をくっつけて表示してるんだけど、concatを使って、
mysql> select A,concat(round(sum(C)/sum(B)*100,1),"%") as RIEKIRITU from hoge group by A order by RIEKIRITU desc; +--------+-----------+ | A | RIEKIRITU | +--------+-----------+ | 山田 | 40.0% | | 木村 | 154.5% | | 伊藤 | 109.7% | +--------+-----------+ 3 rows in set (0.00 sec)
とすると、並び順がおかしくなる。。
多分に、RIEKIRITUを文字列とみなして、それの降順で表示しちゃってるからだと思うんだけど、これを数値の降順として表示することはできるのかな?
http://q.hatena.ne.jp/1199348584
まず、ワークシート名の一覧を取得します。
Yahoo!で、「vba excel ワークシート名 取得」で検索すると、
http://www.relief.jp/itnote/archives/000960.php
が出てきました。
ここに載っているサンプルは、シート名をセルに代入していくようなので、これをDebug.Printするようにしてみます。
Sub test()
Dim objSheet As Object
For Each objSheet In ActiveWorkbook.Sheets
Debug.print objSheet.Name
Next
End Sub

ん、いい感じ(^^)
次は、「シート名が「はてな」か、半角数字の場合」ですね。
シート名が「はてな」は簡単だけど、「半角数字の場合」が難しいですね。
他の言語だと、「正規表現」なんかを使うところだと思うので、Yahoo!で「vba excel 正規表現」で検索してみると、、、
http://codezine.jp/a/article/aid/1655.aspx
によると、「RegExpオブジェクト」なるものを参照設定しないと駄目らしい。
この手の「参照設定」は、プログラムを使う際設定しないといけないと思うので、あんまり好きじゃないんですよね~。
「このプログラムを使いたいなら、事前にいろいろ設定して~」というのは、めんどくさいですし。
なので、参照設定とかを出来るだけ使わないでやってみることにします。
Yahoo!で「vba excel 半角数字か」で検索すると、
http://oshiete1.goo.ne.jp/c257.html?start=7296
が見つかりました。この中から、
半角数字かどうかの判定
http://oshiete1.goo.ne.jp/qa2404154.html
を見ていくと、
http://homepage1.nifty.com/rucio/main/technique/InputChk2.htm
が紹介されています。
これによると、IsNumericなる関数があるようですが、それでは対象文字列が「3D2」などの場合、数字じゃないのに数字とみなされてしまうようなので、一文字ずつばらしてみていったほうが良いみたいですね。
対象の数字が半角数字かどうかをチェックする関数 hankakusuujicheckを作ってみます。
hankakusuujicheck(“123″)→true
hankakusuujicheck(“abc”)→false
こんな感じになるように。
Sub test()
If hankakusuujicheck("123") Then
Debug.Print "123は半角数字だよ"
Else
Debug.Print "123は半角数字じゃないよ"
End If
If hankakusuujicheck("abc") Then
Debug.Print "abcは半角数字だよ"
Else
Debug.Print "abcは半角数字じゃないよ"
End If
End Sub
Function hankakusuujicheck(ByVal checkvalue As String) As Boolean
Dim i As Integer
For i = 1 To Len(checkvalue)
If Not Mid(checkvalue, i, 1) Like "[0-9]" Then Exit Function
Next
hankakusuujicheck = True
End Function

よいようです(^^)
では、シート名が「はてな」か半角数字の場合、シート名を表示させてみます。
Sub test()
Dim objSheet As Object
Dim Sheetmei As String
For Each objSheet In ActiveWorkbook.Sheets
Sheetmei = objSheet.Name
If Sheetmei = "はてな" Or hankakusuujicheck(Sheetmei) Then
Debug.Print Sheetmei
End If
Next
End Sub
Function hankakusuujicheck(ByVal checkvalue As String) As Boolean
Dim i As Integer
For i = 1 To Len(checkvalue)
If Not Mid(checkvalue, i, 1) Like "[0-9]" Then Exit Function
Next
hankakusuujicheck = True
End Function
Excelのワークシート名を「はてな」「123」「ほげ」に変えて、プログラムを実行してみます。

ん、よさげ(^^)
次に、「当該シート内におけるA列の全行にある共通の文字列を
「ももんが」に置換するというマクロを作ってください。」とのこと。
「A列の全行」となると、まずA1から縦にどこまでデータが入ってるのかを調べないといけないですね。最後の行を探す方法を検索してみます。
Yahoo!で「excel vba セル 最終行」で検索すると、
http://www.moug.net/tech/exvba/0050088.htm
のページが見つかりました。やりたいことそのまんまです(^^)
Sub test()
ActiveSheet.Range("A1").End(xlDown).Select
End Sub
として、ExcelのシートのA列にデータを入れてみて、実行します。

ん、確かに、A列でデータが入ってる最後の行まで移動しました。
これで、A列がどの行までデータが入ってるかは取得できました。
A1からこのセルまでの範囲に入ってる文字列を調べる、ということなので、とりあえず、A1からこのセルまでの値を抜き出してみたいと思います。
A1から上の方法で見つけたセルまでを繰り返す、ってことは、
for i=1 to (今見つけたセルの行番号)
Debug.print(「A(i)」のセルの中身)
next i
こんな感じになると思うけど。今いるセルの行番号ってどうやって取得するのかな?
Yahoo!で「excel vba セル 行番号」で検索。
http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q116001856
によると、ActiveCell.Row で今アクティブなセルの行番号が取得できるらしいので、
Sub test()
Dim lastgyou As Integer
Dim i As Integer
ActiveSheet.Range("A1").End(xlDown).Select
lastgyou = ActiveCell.Row
For i = 1 To lastgyou
Debug.Print (ActiveSheet.Cells(i, 1).Value)
Next
End Sub
lastgyouという変数に、最後の行の行番号を代入して、
for ループで、1~lastgyouまでのセルの値を表示してみます。

ん、いいんでないかい?(^^)
さて、次は「A列に共通の文字列」を探す、ということなんですが。質問者さんの例で、
「とびうお01」
「とびうお02」
「新とびうお」
とありますが、人間が見て「とびうお」が共通してるなってのは一目瞭然なんですけど、これをプログラムでどうやらせるか、ですよね。
まず、共通してる文字列といっても、「と」「び」「う」「お」の1文字ずつみても、それぞれ共通してますよね?これを「ももんが」に置き換えると、
「ももんがももんがももんがももんが01」
「ももんがももんがももんがももんが02」
「新ももんがももんがももんがももんが」
ってなことにもなりますけど、これは質問者さんの意図するとこではない
ですので、共通していて一番長い文字列、を「ももんが」に置き換える、としてみます。
「全てのA列に共通の文字列」ということは、
「A1に入ってる値の一部分が、A2以降のすべてに含まれている文字列」
と考えればよいですよね?
例えば、上の例ですと、「とびうお01」の「とびうお」という文字列が、それ以降の行に全て入っていますから、これが共通する文字列となります。
それでは、とりあえず、指定した文字列がA2から最後の行までに含まれているかどうかを調べるサブルーチンを作ってみます。
Sub test()
If zenbuarukadouka("とびうお") Then
Debug.Print "とびうおはA2以降全てにあります"
Else
Debug.Print "とびうおはA2以降でない行もあります"
End If
If zenbuarukadouka("とびうお0") Then
Debug.Print "とびうお0はA2以降全てにあります"
Else
Debug.Print "とびうお0はA2以降でない行もあります"
End If
End Sub
Function zenbuarukadouka(ByVal check As String) As Boolean
Dim lastgyou As Integer
Dim i As Integer
ActiveSheet.Range("A1").End(xlDown).Select
lastgyou = ActiveCell.Row
For i = 2 To lastgyou
If InStr(ActiveSheet.Cells(i, 1).Value, check) = 0 Then Exit Function
Next
zenbuarukadouka = True
End Function

zenbuarukadouka()は、与えられた文字列がA2~最後の行まで含まれているかどうかをチェックします。InStr関数を使って、各セルの値に含まれていなかったら、その場で関数を終了しFalseを返します。全ての行に含まれていたら(つまり、for nextがすべてループしたら)Trueの値を返します。
さて、ということで、このzenbuarukadouka()に、A1の文字列の一部分を与えていけばいいわけですけど、、、
例えば、A1に「あいうえお」という文字が入っていたとします。
A2以降に共通に入っている文字列を zenbuarukadouka()で調べる際に、どのような値を渡せばよいかですが、
長い順から調べていくということですから、5文字の
zenbuarukadouka(“あいうえお”)ですね。次に長いのは4文字の
zenbuarukadouka(“あいうえ”)と、同じく4文字の
zenbuarukadouka(“いうえお”)ということになります。
次は3文字の
zenbuarukadouka(“あいう”)
zenbuarukadouka(“いうえ”)
zenbuarukadouka(“うえお”)
続いて2文字、
zenbuarukadouka(“あい”)
zenbuarukadouka(“いう”)
zenbuarukadouka(“うえ”)
zenbuarukadouka(“えお”)
最後に1文字、
zenbuarukadouka(“あ”)
zenbuarukadouka(“い”)
zenbuarukadouka(“う”)
zenbuarukadouka(“え”)
zenbuarukadouka(“お”)
となります。
これを上から順に調べていって、全ての行で見つかったものがあれば、それが一番長い共通の文字列となります。
Sub test()
Dim i As Integer
Dim j As IntegerDim A1 As String
A1 = "あいうえお"
For i = Len(A1) To 1 Step -1
For j = 1 To Len(A1) - i + 1
Debug.Print Mid(A1, j, i)
Next j
Next i
End Sub
A1という変数に入れられた文字列で、チェック対象となる文字列をDebug.printするプログラムです。

iとjという変数がありますが、iは5~1へ変化します。これは、A1の文字数である5から一番短い文字数の1まで変化する、ということです。
jは、A1の文字列を抜き出す際、何文字目から抜き出すか、を表しています。
これは i によって、値の範囲が変わります。
iが5、つまり、A1の文字数と一緒であれば、jは1~1、つまり、1文字目から5文字抜き出します(あいうえお)。
iが4の場合は、jは1~2、つまり、1文字目から4文字抜き出し(あいうえ)、次に2文字目から4文字抜き出します(いうえお)。
以下、3文字の場合、2文字の場合、1文字の場合も同じように1~3文字目、1~4文字目、1~5文字目が抜き出されるようになっています。
あとは、A1から最後の行まで、見つけた文字列で置換していく部分さえ出来れば完成かな?
A1から最後の行まで、指定した文字列を”ももんが”に置換するプログラムは以下のとおりです。
Sub test()
chikan ("とびうお")
End Sub
Function chikan(ByVal henkanmoto As String)
Dim i As Integer
Dim lastgyou As Integer
ActiveSheet.Range("A1").End(xlDown).Select
lastgyou = ActiveCell.Row
For i = 1 To lastgyou
ActiveSheet.Cells(i, 1).Value = Replace(ActiveSheet.Cells(i, 1).Value, henkanmoto, "ももんが")
Next i
End Function
最後の行を求めるのは、さっきまでと同じやりかたで、for nextでループして、全ての行のセルをReplaceを使って、変換元の文字列を”ももんが”に置き換えます。

というデータで実行すると、

となります。
これで必要な機能は全て揃ったので、全部を組み合わせたプログラムを作ってみます。
Sub text()
Dim objSheet As Object
Dim Sheetmei As String
Dim A1 As String
Dim i As Integer
Dim j As Integer
Dim nukidashi As String
Dim atta As Integer
'現在開いているBookのすべてのSheetをチェックします
For Each objSheet In ActiveWorkbook.Sheets
'Sheetmei にシート名を代入します
Sheetmei = objSheet.Name
'もし、シート名が「はてな」、もしくは、半角数字の場合、
If Sheetmei = "はてな" Or hankakusuujicheck(Sheetmei) Then
'そのシート名のシートを選択します。
ActiveWorkbook.Sheets(Sheetmei).Select
'今選択されているシートのA1のセルの値を、A1という変数に代入します。
A1 = ActiveSheet.Cells(1, 1).Value
'見つかった場合、ループから抜け出すための変数を0にしておきます。
atta = 0
'A1の変数の文字列を長い順に抜き出します。
For i = Len(A1) To 1 Step -1
For j = 1 To Len(A1) - i + 1
'nukidashiという変数に、A1の文字列を抜き出したものを代入します。
nukidashi = Mid(A1, j, i)
'もし、nukidashiの文字列がA2以降、すべての行に入っていたら、
'attaという変数を 1 にして、内側のループを抜けます
If zenbuarukadouka(nukidashi) Then
atta = 1
Exit For
End If
Next j
'もし、attaが1の場合、もう見つかっているので、外側のループも抜けます
If atta = 1 Then Exit For
Next i
'もし、attaが1の場合、共通する文字列が見つかったので、A2以降すべてを置き換えます
If atta = 1 Then
chikan (nukidashi)
End If
End If
Next
End Sub
Function hankakusuujicheck(ByVal checkvalue As String) As Boolean
Dim i As Integer
For i = 1 To Len(checkvalue)
If Not Mid(checkvalue, i, 1) Like "[0-9]" Then Exit Function
Next
hankakusuujicheck = True
End Function
Function zenbuarukadouka(ByVal check As String) As Boolean
Dim lastgyou As Integer
Dim i As Integer
ActiveSheet.Range("A1").End(xlDown).Select
lastgyou = ActiveCell.Row
For i = 2 To lastgyou
If (InStr(ActiveSheet.Cells(i, 1).Value, check) = 0) Then Exit Function
Next
zenbuarukadouka = True
End Function
Function chikan(ByVal henkanmoto As String)
Dim i As Integer
Dim lastgyou As Integer
ActiveSheet.Range("A1").End(xlDown).Select
lastgyou = ActiveCell.Row
For i = 1 To lastgyou
ActiveSheet.Cells(i, 1).Value = Replace(ActiveSheet.Cells(i, 1).Value, henkanmoto, "ももんが")
Next i
End Function
ループでシート名を抜き出した後、「はてな」か半角数字の場合、そのシートを選択する必要があるので、
ActiveWorkbook.Sheets(Sheetmei).Select
で選択しています。
それと、文字列の抜き出しを行っている際に、途中で見つかった場合、attaという変数を用いて、ループを抜け、その後の置換処理を行うかどうかも、attaの変数の値を見て判断しています。
では、実際にデータをいれてこのプログラムを動かしてみます。

という状態で実行してみます。

正常に動作しました(^^)
このxlsファイルを置いておきます→vbastudy_0005.xls