Quantcast
Browsing Latest Articles All 136 Live
Mark channel Not-Safe-For-Work? (0 votes)
Are you the publisher? or about this channel.
No ratings yet.
Articles:

Lraravelヘルパ関数の配列とオブジェクトについて

はじめに

今回はLraravelヘルパ関数の配列とオブジェクトについてまとめます。

連想配列の関数について(抜粋)

Arrクラスは配列を操作する時に便利に使えます。

①Arr::addメソッドは指定キー/値のペアをそのキーが存在していない場合とnullがセットされている場合に、配列に追加します。

useIlluminate\Support\Arr;$array=Arr::add(['name'=>'skirt'],'price',5000);// ['name' => 'Desk', 'price' => 5000]$array=Arr::add(['name'=>'skirt','price'=>null],'price',5000);// ['name' => 'Desk', 'price' => 5000]

②Arr::getメソッドは「ドット」記法で指定した値を深くネストされた配列から取得します。

useIlluminate\Support\Arr;$array=['products'=>['skirt'=>['price'=>5000]]];$price=Arr::get($array,'products.skirt.price');// 5000

③Arr::forgetメソッドは「ドット記法」で指定キー/値のペアを深くネストされた配列から取り除きます。

useIlluminate\Support\Arr;$array=['products'=>['skirt'=>['price'=>5000]]];Arr::forget($array,'products.skirt');// ['products' => []]

終わりに

いかがでしたでしょうか。
よく使いそうなものを抜粋しましたが公式ドキュメントに他にも記載してあるので確認してみてください。

参考:ヘルパ 7.x Laravel

Windows10のイベントログを取得してソートしてループ処理するPowerShell

各種イベントログを取得して時系列にソートを掛けたい要件があったので、サンプルを作った。

今回はSystemログを2回取得して配列A・Bに格納しているが、
イベントログのデータ構造は、ApplicationログもSecurityログも同じ(記事の下の付録参照)。
よって、別々の種類のイベントログであってもシンプルに配列を結合することで実現が可能。

PowerShellは配列同士の"+"演算で配列の結合ができるため実装が大変シンプルになる。
(同じデータ構造を持ってくれているおかげで余計にデータを捏ねる必要がなくなって好都合だ)

以下のスクリプトは$todayの日付以降の最新のログを10件取得する仕様になっている。

sortedArrayEventLog.ps1
$today=Get-Date-Format"yyyy年MM月dd日"$lognum=10# 結合した配列を作るための配列A$Events_A=Get-EventLog-LogNameSystem-After$today-Newest$lognum# 結合した配列を作るための配列B$Events_B=Get-EventLog-LogNameSystem-After$today-Newest$lognum# 配列同士を結合して新しい配列を作る$Events_Set=$Events_A+$Events_B# イベントログの発生時刻で降順にソートして、一つ一つループ処理する$Events_Set.GetEnumerator()|Sort-Object-PropertyTimeGenerated-Descending|ForEach-Object{Write-Host$_.InstanceId}

バッチファイルを叩いて起動する

sortedArrayEventLog.bat
setTODAY=%DATE:/=%setPATH=%C:fuga%setLOGPATH=%C:hoge%echo%DATE%%TIME%>>%LOGPATH%\sortedArrayEventLog_bat_%TODAY%.logpowershell-ExecutionPolicyRemoteSigned-File%PATH%sortedArrayEventLog.ps1-verbrunas>>%LOGPATH%\sortedArrayEventLog_bat_%TODAY%.logpause

付録

なお、イベントログのデータ構造は以下の通り。
EventLogのデータ構造が同じであると示したいだけなのだが…ちょっとくどい。

  • システムログのデータ構造
EventLog_System
$today=Get-Date-Format"yyyy年MM月dd日"PS>Get-EventLog-LogNameSystem-After$today-Newest1|Get-Member-MemberTypePropertiesTypeName:System.Diagnostics.EventLogEntry#System/Microsoft-Windows-Kernel-Processor-Power/37NameMemberTypeDefinition------------------------CategoryPropertystringCategory{get;}CategoryNumberPropertyint16CategoryNumber{get;}ContainerPropertySystem.ComponentModel.IContainerContainer{get;}DataPropertybyte[]Data{get;}EntryTypePropertySystem.Diagnostics.EventLogEntryTypeEntryType{get;}IndexPropertyintIndex{get;}InstanceIdPropertylongInstanceId{get;}MachineNamePropertystringMachineName{get;}MessagePropertystringMessage{get;}ReplacementStringsPropertystring[]ReplacementStrings{get;}SitePropertySystem.ComponentModel.ISiteSite{get;set;}SourcePropertystringSource{get;}TimeGeneratedPropertydatetimeTimeGenerated{get;}TimeWrittenPropertydatetimeTimeWritten{get;}UserNamePropertystringUserName{get;}EventIDScriptPropertySystem.ObjectEventID{get=$this.get_EventID()-band0xFFFF;}
  • アプリケーションログのデータ構造
EventLog_Application
PS>Get-EventLog-LogNameApplication-After$today-Newest1|Get-Member-MemberTypePropertiesTypeName:System.Diagnostics.EventLogEntry#Application/VSS/8224NameMemberTypeDefinition------------------------CategoryPropertystringCategory{get;}CategoryNumberPropertyint16CategoryNumber{get;}ContainerPropertySystem.ComponentModel.IContainerContainer{get;}DataPropertybyte[]Data{get;}EntryTypePropertySystem.Diagnostics.EventLogEntryTypeEntryType{get;}IndexPropertyintIndex{get;}InstanceIdPropertylongInstanceId{get;}MachineNamePropertystringMachineName{get;}MessagePropertystringMessage{get;}ReplacementStringsPropertystring[]ReplacementStrings{get;}SitePropertySystem.ComponentModel.ISiteSite{get;set;}SourcePropertystringSource{get;}TimeGeneratedPropertydatetimeTimeGenerated{get;}TimeWrittenPropertydatetimeTimeWritten{get;}UserNamePropertystringUserName{get;}EventIDScriptPropertySystem.ObjectEventID{get=$this.get_EventID()-band0xFFFF;}
  • セキュリティログのデータ構造
EventLog_Security
PS>Get-EventLog-LogNameSecurity-After$today-Newest1|Get-Member-MemberTypePropertiesTypeName:System.Diagnostics.EventLogEntry#Security/Microsoft-Windows-Security-Auditing/5379NameMemberTypeDefinition------------------------CategoryPropertystringCategory{get;}CategoryNumberPropertyint16CategoryNumber{get;}ContainerPropertySystem.ComponentModel.IContainerContainer{get;}DataPropertybyte[]Data{get;}EntryTypePropertySystem.Diagnostics.EventLogEntryTypeEntryType{get;}IndexPropertyintIndex{get;}InstanceIdPropertylongInstanceId{get;}MachineNamePropertystringMachineName{get;}MessagePropertystringMessage{get;}ReplacementStringsPropertystring[]ReplacementStrings{get;}SitePropertySystem.ComponentModel.ISiteSite{get;set;}SourcePropertystringSource{get;}TimeGeneratedPropertydatetimeTimeGenerated{get;}TimeWrittenPropertydatetimeTimeWritten{get;}UserNamePropertystringUserName{get;}EventIDScriptPropertySystem.ObjectEventID{get=$this.get_EventID()-band0xFFFF;}

JavaScript: Array.reduce()メソッドで配列要素を入れ替える

Array.prototype.reduce()は、応用範囲の広い配列操作のメソッドです。ただ、与えられるパラメータの数も多く、使い方がわかりにくいきらいは否めません。配列要素を入れ替える例がわかりやすそうかと思いついたので採り上げます。

数値要素の合計を求める

簡単な例からはじめます。よく使われるのが、数値の配列要素の合計です。アロー関数式=>で与えたコールバック関数の第1引数(sum)が結果の集計で、第2引数の要素(num)を順に取り出して集計にまとめます。合計の場合には、集計結果に要素の値を加えてゆくということです。

constsum=[0,1,2,3,4].reduce((sum,num)=>sum+num);console.log(sum);// 10

要素に処理を加えた新たな配列を返す

Array.prototype.reduce()の第2引数は集計の初期値です。前項の例では省きました。すると、インデックス0の値を初期値として、インデックス1から処理がはじまるのです。でも、集計値が配列などのオブジェクトになる場合には、初期値を与えなければなりません。

数値の配列要素を、それぞれ2乗するのがつぎの例です。集計の配列(resultArray)に要素を加えるときは、Array.prototype.push()を用いるのが通常でしょう。ここではあえて、コールバック関数の第3引数(id)を使ってみました。

constarray=[0,1,2,3,4].reduce((resultArray,num,id)=>{resultArray[id]=num**2;returnresultArray;},[]);console.log(array);// [0, 1, 4, 9, 16]

もっとも、各配列要素への処理が加えられた同じ要素数の配列を返すなら、Array.prototype.map()メソッドの方が端的です。

constarray=[0,1,2,3,4].map((num)=>num**2);

条件に合った要素からなる配列を返す

Array.reduce()は、Array.map()と異なり、返す配列の要素数は問われません。たとえば、数値の配列から偶数値だけの配列をつくって返すこともできます。この場合、要素数が変わりますので、集計配列(resultArray)に条件に合った値を加えるのはArray.push()メソッドです。

constarray=[0,1,2,3,4].reduce((resultArray,num)=>{if(num%2===0){resultArray.push(num);}returnresultArray;},[]);console.log(array);// [0, 2, 4]

ただ、条件に合った値が収められた配列をつくるには、Array.prototype.filter()メソッドが用意されています。

constarray=[0,1,2,3,4].filter((num)=>num%2===0);

配列要素を入れ替える

Array.reduce()の処理は一応わかっても、他の簡単なメソッドで済ませられるのならどこがいいのか、と思われるかもしれません。けれど逆に考えれば、Array.map()Array.filter()がなかったとしても、Array.reduce()で同じ処理はできます。それが、応用範囲の広いメソッドだという理由です。

とはいえ、Array.reduce()メソッドのありがたみはわかりません。思いついた例が、配列要素を入れ替える処理です。これまでのメソッドで扱おうとすれば、Array.prototype.splice()を使うことになるでしょう。その場合、要素の削除と挿入のふたつの操作が必要です。いずれも、もとの配列の対象要素からあとのインデックスがずれてしまいます。

この要素入れ替えの処理を、つぎのような関数(replaceArrayElements())として定めてみましょう。

replaceArrayElements(配列,入れ替え先インデックス,入れ替えもとインデックス)

Array.reduce()メソッドのコールバック関数は、第4引数にもとの配列が受け取れます。そして、メソッドの戻り値は処理を終えた新たな配列です。つまり、もと配列のインデックスは崩れません。したがって、以下のように入れ替えるインデックスの要素を、差し替えさえすればよいのです。

戻り値の配列(resultArray)には、あえてインデックスによるブラケット[]アクセスもArray.push()メソッドも用いず、スプレッド構文...で新たな配列をつくってみました。これでインデックスのずれを気にすることなく、要素の入れ替えができます。

functionreplaceArrayElements(array,targetId,sourceId){returnarray.reduce((resultArray,element,id,originalArray)=>[...resultArray,id===targetId?originalArray[sourceId]:id===sourceId?originalArray[targetId]:element],[]);}constarray=replaceArrayElements([0,1,2,3,4],3,1);console.log(array);// [0, 3, 2, 1, 4]

Array.reduce()のパラメータすべてを用いて、このメソッドがどう役立つか示せたのではないでしょうか。

もっとも、配列要素の入れ替えというお題については、分割代入を使った方がもう少し簡潔になりそうです。なお、配列はスプレッド構文...で複製しました。

functionreplaceArrayElements(array,targetId,sourceId){constcloneArray=[...array];[cloneArray[targetId],cloneArray[sourceId]]=[array[sourceId],array[targetId]];returncloneArray;}

Java Scriptの基礎

JavaScriptとは

JavaScriptとは、プログラミング言語の一つで、1990年代中盤に登場しました。
サイトのプルダウンや画面を更新しないでサーバーと通信したい時に使われます。
略称は、JSです。

HTMLへの導入方法

htmlファイルと同じ階層にJSファイルがある場合htmlファイルに以下のように記述するとJSファイルが読み込まれます。

index.html
<head><script src="script.js"></script></head>

headタグ内にある、scriptタグsrc属性にファイル名を記述します。JSの拡張子は.jsです。

window.alert()

ブラウザにアラートを表示させるメソッドです。
表示させたい値を引数にとります。また、変数でも可能です。

console.log()

ブラウザのコンソールにテキストを表示させるメソッドです。
表示させたい値を引数にとります。また、変数でも可能です。

変数での記述方法は以下になります。

script.js
varteam="鹿島アントラーズ";console.log(team+"の勝利です。");

コンソールが面に 鹿島アントラーズの勝利です。 が表示されます。

変数宣言について

varはES6バージョンの書き方です。
それ以降の変数宣言の仕方としては。
・let
・const
の二つが用いられます。

letは、後で書き換えられ変数宣言です。
constは、後で書き換えられない変数宣言です。

条件分岐

script.js
letnumber=100;if(number%15==0){console.log(number+"は、3と5の倍数です。");}elseif(number%3==0){console.log(number+"は、3の倍数です。");}elseif(number%5==0){console.log(number+"は、5の倍数です。");}else{console.log(number+"は、3の倍数でも、5の倍数でもありません");}

上記のように記述するとコンソールに、100は、5の倍数です。 と表示されます。
ifの後のカッコ内に条件式を記述します。その条件に当てはまった場合は、波カッコ無いの記述が処理されます。
最初の条件式が偽だった場合は、else if以降に処理が移ります。その条件が真だったら波カッコ内の処理を開始、偽だった場合はさらに次に進みます。elseはどの条件にも当てはまらなかった場合に出力したい処理を記述します。

配列

JSには配列という概念があります。

script.js
letname=["takuya","shingo","tsuyoshi","masahiro","GORO"];console.log(name);##コンソールでの表示(5) ["takuya","shingo","tsuyoshi","masahiro","GORO"]---------配列の数を取得---------lengthメソッドを使います。------------------------------console.log(name.length);##コンソールでの表示5---------配列の最後に要素を追加---------pushメソッドを使います。-------------------------------------name.push("morikun");console.log(name);///"morikun"が追加されます。##コンソールでの表示(6) ["takuya","shingo","tsuyoshi","masahiro","GORO","morikun"]---------配列の最後の要素を削除---------popメソッドを使います。-------------------------------------name.pop();console.log(name);///"morikun"が削除されます。##コンソールでの表示(5) ["takuya","shingo","tsuyoshi","masahiro","GORO"]---------配列の最初の要素を削除---------shiftメソッドを使います。-------------------------------------name.shift();console.log(name);///"takuya"が削除されます。##コンソールでの表示(4) ["shingo","tsuyoshi","masahiro","GORO"]

popメソッドとshiftメソッドに指定して複数の要素を削除することはできません。

オブジェクト

波カッコを使ってオブジェクトを生成します。
{}内に名前と値をセットにして管理します。このセットのことをプロパティと言います。
最初からオブジェクトにプロパティを定義して生成することもできますし、空の波カッコで生成することもできます。

script.js
lettakuya={name:"kimura",age:40,team:"SMAP"};console.log(takuya);###コンソールでの表示{name:"kimura",age:40,team:"SMAP"}///名前がteamにあたる値を取得console.log(takuya.team);###コンソールでの表示SMAP///名前がageにあたる値を更新takuya.age=47;console.log(takuya.age);###コンソールでの表示47

for文

繰り返しの構文です。

for(let i = 0; i < 繰り返す回数; i += 1) {繰り返す処理}と記述します。

script.js
num=1;for(leti=0;i<10;i+=1){console.log(num+"回目の出力になります!");num+=1;}###コンソールでの表示1回目の出力です。2回目の出力です。3回目の出力です。4回目の出力です。5回目の出力です。6回目の出力です。7回目の出力です。8回目の出力です。9回目の出力です。10回目の出力です。

Vue.jsのv-forの見落としがちなkey属性

はじめに

Vue.jsのv-for正しい使い方で実装できていますか。
細かい話ですがつい先日Vue.jsのv-for実装する時、他のソースコード見てみるとkey属性が指定されていませんでした。
後ほど説明しますが例外を除いてはkey属性を指定することが奨励されています。
(バージョン2.2.0以降の話なのでそれ以前のもので実装されている方は軽い気持ちでで読んでください。)

Vue.jsのv-forのkey属性については公式ドキュメントにも以下のように書いてあります。

繰り返される DOM の内容が単純な場合や、性能向上のために標準の動作に意図的に頼る場合を除いて、可能なときはいつでもv-forにkey属性を与えることが推奨されます。

引用:リストレンダリング — Vue.js

なぜkey属性を指定することが奨励されているのか

これはいかなるデータもコンポーネントへ自動的に渡すことはありません。なぜなら、コンポーネントはコンポーネント自身の隔離されたスコープを持っているからです。反復してデータをコンポーネントに渡すためには、プロパティを使うべきです。

引用:リストレンダリング — Vue.js

公式ドキュメントにはこう書いてありますと言ってしまえばその限りなのですが、keyを指定するとVue.jsが各要素を効率よく追跡できるようになりパフォーマンス向上するからです。

key属性を指定したソースコード

<ul id="example-1">
  <li v-for="item in items" :key="item.message">
    {{ item.message }}
  </li>
</ul>

引用:v-for で配列に要素をマッピングする

【注意】v-forにkeyを指定してはいけないパターン

Vue.jsのv-forのkey属性を推していましたが指定してはいけないパターンがあります。
keyに指定するのは一意の値である必要があり、これが一意の値にならない場合はkey属性を指定してはいけません。
正しく動作しないなどのエラーになるのでお気をつけください。

最後に

いかがでしたでしょうか。
なんとなくや前のソースコード参考にという形で実装していると思わぬ落とし穴もあるので、常に疑いながら実装することは大事かもしれませんね。

【Java入門】配列の操作について(1次元配列、2次元配列の宣言、インスタンス化、初期化および使用)

目的

Java言語を含めたプログラミングの学習を始めたばかりの方、既学習者の方は復習用に、
今回は配列について学ぶために書いています。

【Java入門目次】
変数と型
型変換
変数のスコープ
・文字列の操作(準備中)
・配列の操作 ←今ここ
・演算子(準備中)
・条件分岐(準備中)
・繰り返し処理(準備中)
・クラスについて(準備中)
・抽象クラス(準備中)
・インターフェース(準備中)
・カプセル化(準備中)
・モジュールについて(準備中)
例外処理について

配列とは

変数は、一つの変数に対して一つの値データを入れる入れ物ですが(変数についてはこちら)、

配列を使用することで、同じデータ型の複数のデータを一つの配列で管理することができる。

500個分の値がある時、変数の場合では500個分用意しなければいけませんが、
配列の場合では、一つ配列を用意してその中に500個分のデータを格納できるということです。

また、その配列の中のデータを並び替えをしたり、一番大きな値を取得したりなども容易に出来てしまいます。

1次元配列の宣言

配列を作成する時は、変数と同様にまずどのような値を扱うかのデータ型を決め、名前をつけて宣言する必要があります。

データ型[] 配列名と宣言します。

Main.java
classMain{publicstaticvoidmain(String[]args){int[]numbers;// int型の値を扱える配列の宣言// [] は、配列名の後ろでも構わない。Stringnames[];// String型の値を扱える配列の宣言}}

1次元配列のインスタンス化

宣言した配列にいくら値を詰めるのか、領域の確保をしてあげなければいけません。
new [要素数]での確保する領域の大きさ決めます。

Main.java
classMain{publicstaticvoidmain(String[]args){int[]numbers;// int型の値を扱える配列の宣言Stringnames[];// String型の値を扱える配列の宣言numbers=newint[50];// numbers配列に50個分の値を格納するための領域を確保names=newString[3];// name配列に3個分の値を格納するための領域を確保}}

配列の宣言と領域の確保を同時に行う事も可能です。

Main.java
classMain{publicstaticvoidmain(String[]args){int[]numbers=newint[50];// int型の値を扱える配列の宣言と50個分の領域を確保Stringnames[]=newString[3];// String型の値を扱える配列の宣言と3個分の領域を確保}}

要素数を指定しなければ、コンパイルエラーになるので注意しましょう。

Main.java
classMain{publicstaticvoidmain(String[]args){int[]numbers=newint[];// 要素数を指定していないため、コンパイルエラーStringnames[]=newString[];// 要素数を指定していないため、コンパイルエラー}}

配列の要素数は後から変更することは出来ず、
最初に配列の要素数を5で指定した場合は、その後の要素数はずっと5で固定されるので覚えておきましょう。

また、要素数は整数ではないとはないといけない点も覚えおきましょう。

1次元配列の添え字(インデックス)と値の格納

配列の宣言、インスタンス化で値を格納する準備が整いました。
配列への値を代入は、添え字(インデックス)を使用します。
添え字(インデックス)は、配列の各々の要素につけられた通し番号で、0から始まります。

Main.java
classMain{publicstaticvoidmain(String[]args){int[]numbers=newint[5];// int型の5つの要素が格納出来るnumbers配列を定義numbers[0]=100;// 1番目には、100numbers[1]=200;// 2番目には、200numbers[2]=300;// 3番目には、300numbers[3]=400;// 4番目には、400numbers[4]=500;// 5番目には、500}}

配列の各要素にアクセスする際も添え字(インデックス)を用います。

Main.java
classMain{publicstaticvoidmain(String[]args){int[]numbers=newint[5];// int型の5つの要素が格納出来るnumbers配列を定義numbers[0]=100;// 1番目には、100numbers[1]=200;// 2番目には、200numbers[2]=300;// 3番目には、300numbers[3]=400;// 4番目には、400numbers[4]=500;// 5番目には、500System.out.println(numbers[0]);// 100 が出力されるSystem.out.println(numbers[3]);// 400 が出力される}}

1次元配列の初期化

上記までは配列の宣言、領域の確保、値(初期値)の代入を順を追って行っていましたが、

配列の宣言、領域の確保、値(初期値)の代入を全て同時に行うことが出来ます。(配列の初期化)

値を{}で囲み、カンマ区切りで各要素を記述していきます。
データ型 [] 配列名 = {初期値1, 初期値2, 初期値3, 初期値4, 初期値5};

また、作成した要素の数を調べるにはlengthを使用します。
配列名.length;で要素数を取得する事が出来ます。

Main.java
classMain{publicstaticvoidmain(String[]args){int[]numbers={100,200,300,400,500};// 配列の初期化intsize=numbers.length;// numbers配列の要素数を取得 この場合、5が格納されるSystem.out.println(numbers[0]);// 100 と出力されるSystem.out.println(numbers[1]);// 200 と出力されるSystem.out.println(numbers[2]);// 300 と出力されるSystem.out.println(numbers[3]);// 400 と出力されるSystem.out.println(numbers[4]);// 500 と出力されるSystem.out.println(size);// 5 と出力される// この記述方法での配列の初期化も有効int[]id=newint[]{1,2,3};System.out.println(id[0]);// 1 と出力されるSystem.out.println(id[1]);// 2 と出力されるSystem.out.println(id[2]);// 3 と出力されるSystem.out.println(id.length);// 3 と出力される}}

配列の要素外にアクセスした時

配列の要素のアクセスするには添え字(インデックス)を用いてアクセスしていましたが、
配列の要素外にアクセスをしようとしている時、コンパイルエラーにはなりませんが、実行時エラー(例外)が発生します。
詳しくは、例外処理の記事をご覧ください。

こちらでも軽く見てみましょう。

Main.java
classMain{publicstaticvoidmain(String[]args){int[]id={1,2,3,4,5};// 配列の要素数は5個// for文で6回のループを回す = id配列の要素を超えるfor(inti=0;i<6;i++){// 一つずつ出力しているSystem.out.println(id[i]);}System.out.println("id配列の中身を全て出力し終えました。");}}

出力結果は、

Terminal
1
2
3
4
5
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
        at Main.main(Main.java:195)

上記の様に出力されました。
ArrayIndexOutOfBoundsException例外が発生しています。
そして、id配列の中身を全て出力し終えました。と表示されていません。
この様に配列へアクセスしようとした時、要素外にアクセスしている場合、
例外が発生し、途中で処理が止まってしまうので気をつけましょう。

2次元配列の宣言

添え字(インデックス)を2個で管理する2次元配列、それ以上の多次元配列もあります。
今回は2次元配列を説明していきます。

2次元配列では、[]を2つ使って、
データ型[][] 配列名と宣言します。

Main.java
classMain{publicstaticvoidmain(String[]args){int[][]numbers;// int型の値を扱える2次元配列の宣言// [][] は、配列名の後ろでも構わない。Stringstrs[][];// String型の値を扱える2次元配列の宣言}}

2次元配列のインスタンス化

1次元配列の時と同様に、宣言した配列にいくら値を詰めるのか、領域の確保をしてあげなければいけません。

Main.java
classMain{publicstaticvoidmain(String[]args){int[][]numbers;// int型の値を扱える2次元配列の宣言Stringstrs[][];// String型の値を扱える2次元配列の宣言// 3つの要素をもつ配列で、numbers[0]からnumbers[2]の各要素に、4つの要素をもてる領域を確保numbers=newint[3][4];// 2つの要素をもつ配列で、strs[0]からstrs[1]の各要素に、2つの要素をもてる領域を確保strs=newString[2][2];}}

1次元配列と同様に、
配列の宣言と領域の確保を同時に行う事も可能。

Main.java
classMain{publicstaticvoidmain(String[]args){// int型の値を扱える2次元配列の宣言と領域を確保int[][]numbers=newint[3][4];// String型の値を扱える2次元配列の宣言と領域を確保Stringstrs[][]=newString[2][2];}}

また、1次元目の配列の領域確保のみを行うことも可能。
その場合、後から2次元目の要素数を決めることが出来ます。

Main.java
classMain{publicstaticvoidmain(String[]args){int[][]array;// int型の値を扱える2次元配列の宣言array=newint[3][];// 3つの要素をもつ配列の領域を確保array[0]=newint[5];// arrayの1番目の配列は5つの要素を格納できるarray[1]=newint[3];// arrayの2番目の配列は3つの要素を格納できるarray[2]=newint[4];// arrayの3番目の配列は4つの要素を格納できる// 配列の宣言と領域の確保を同時に行った時も同様String[][]strs=newString[2][];// String型の配列の宣言、領域確保strs[0]=newString[6];// strsの1番目の配列は6つの要素を格納できるstrs[1]=newString[3];// strsの2番目の配列は3つの要素を格納できる}}

2次元配列の場合も要素数を後から変更することはできず、要素数は整数ではないといけません。

2次元配列の添え字(インデックス)と値の格納

1次元配列と同様に添え字(インデックス)を用いて値の格納をします。

Main.java
classMain{publicstaticvoidmain(String[]args){int[][]numbers=newint[2][2];// int型の値を扱える2次元配列の宣言numbers[0][0]=100;numbers[0][1]=200;numbers[1][0]=300;numbers[1][1]=400;System.out.println(numbers[0][0]);// 100 と出力されるSystem.out.println(numbers[0][1]);// 200 と出力されるSystem.out.println(numbers[1][0]);// 300 と出力されるSystem.out.println(numbers[1][1]);// 400 と出力される}}

2次元配列の初期化

2次元配列でも、
配列の宣言、領域の確保、値(初期値)の代入を全て同時に行うことが出来ます。(配列の初期化)

同様に値を{}で囲み、カンマ区切りで各要素を記述していきます。
データ型 [][] 配列名 = {
{初期値1, 初期値2, 初期値3, 初期値4, 初期値5},
{初期値6, 初期値7, 初期値8, 初期値9, 初期値10}
};

また、作成した要素の数を調べるにはlengthを使用します。
配列名.length;で要素数を取得する事が出来る。
さらにその配列の中の配列の長さを取得するには、
配列名[添え字(インデックス)].lengthで要素数を取得できます。

Main.java
classMain{publicstaticvoidmain(String[]args){// 1次元配列と同様に、配列の宣言、領域の確保、値の代入を一度に行うも可能。int[][]numbers={{1,2,3,4,5},{6,7,8,9,10},{11,12,13},{16,17}};System.out.println("numbers[0][4]の値 : "+numbers[0][4]);// numbers[0][4]の値 : 5 と出力されるSystem.out.println("numbers[3][0]の値 : "+numbers[3][0]);// numbers[3][0]の値 : 16 と出力されるSystem.out.println("numbersの長さ : "+numbers.length);// numbersの長さ : 4 と出力されるSystem.out.println("numbers[0]の長さ : "+numbers[0].length);// numbers[0]の長さ : 5 と出力されるSystem.out.println("numbers[2]の長さ : "+numbers[2].length);// numbers[2]の長さ : 3 と出力されるSystem.out.println("numbers[3]の長さ : "+numbers[3].length);// numbers[3]の長さ : 2 と出力される}}

配列の使用方法の実例

ここからは1次元配列と2次元配列の簡単な使用方法を紹介します。

小さい順に並び替え

java.util.Arraysクラスを使用するので、最初にimportする事を忘れないでください。
OracleのArraysクラスについてはこちら

Main.java
importjava.util.Arrays;classMain{publicstaticvoidmain(String[]args){// numbers配列を初期化(中の整数の順番はランダム)int[]numbers={10,1,5,6,9};// Arraysクラスのsortメソッドを用いて小さい順に並び替えをするArrays.sort(numbers);// numbers配列を一つずつ出力for(intnumber:numbers){System.out.print(number+" ");// 1 5 6 9 10 と出力される}}}

ランダムな整数が格納されていたnumbersは、sortメソッドを用いることで小さい順に並び替えできています。

次は文字列を並び替えてみます。

Main.java
importjava.util.Arrays;classMain{publicstaticvoidmain(String[]args){// names配列を初期化(中の名前の順番はランダム)String[]names={"tanaka","abe","suzuki","maeda"};// Arraysクラスのsortメソッドを用いてアルファベット順に並び替えをするArrays.sort(names);// names配列を一つずつ出力for(Stringname:names){System.out.print(name+" ");// abe maeda suzuki tanaka と出力される}}}

ランダムな文字列が格納されていたnamesは、sortメソッドを用いることでアルファベット順に並び替えできています。

最大値、最小値を取得する

Main.java
classMain{publicstaticvoidmain(String[]args){// 2次元配列numbersを初期化(中の整数の順番はランダム)int[][]numbers={{2,5,6,-10,100,3},{-1000,1,20},{999,12,300,50}};// 最大値を入れるmax変数を定義intmax=0;// 最小値を入れるmin変数を定義intmin=0;// numbers配列の1次元目を一つずつ見ていくfor(inti=0;i<numbers.length;i++){// numbers[0]、numbers[1]、numbers[2]、の中身を一つずつ見ていくfor(intj=0;j<numbers[i].length;j++){// numbers[0]、numbers[1]、numbers[2]の中で、maxより大きい数字があればif(max<numbers[i][j]){// その数値をmax変数に代入max=numbers[i][j];}// numbers[0]、numbers[1]、numbers[2]の中で、minより小さい数字があればif(min>numbers[i][j]){// その数値をmin変数に代入min=numbers[i][j];}}}System.out.println("numbers配列の最大値 : "+max);// numbers配列の最大値 : 999 と出力されるSystem.out.println("numbers配列の最小値 : "+min);// numbers配列の最小値 : -1000 と出力される}}

for文(繰り返し処理に関しては別記事を記載します)を用いて配列の要素を一つずつ比較しています。

大きい数字、小さい数字があれば、その都度最大値max変数、最小値min変数に値を代入しています。

終わりに

簡単にではありますが、配列について学びました。

配列は要素数は固定でしたが、可変であるListというものもあります。
そちらは別記事で取り上げたいと思います。

複数のデータを用いることができるため、使用する機会は多いです。しっかりおさえておきたいですね。

【jQuery】for ループ内で Ajax で取得した値がうまく表示できない

実装したい機能

特定のクラスを持つエレメントから商品IDを取得し
それぞれの商品詳細ページからカテゴリを取得して
対応する商品IDを持つエレメントに表示する。

使用するコード

開発環境
jquery > 1.7.2

表示するページ

index.html
...
<ul><liclass="products pid_111"><p>商品名①</p><pclass="cate"><!--カテゴリを表示したい--></p></li><liclass="products pid_222"><p>商品名②</p><pclass="cate"><!--カテゴリを表示したい--></p></li><liclass="products pid_333"><p>商品名③</p><pclass="cate"><!--カテゴリを表示したい--></p></li></ul>
...

商品詳細ページ

111.html
...
<div><p>商品名①</p><pclass="p_cate">カテゴリ①</p><!--これを取得したい--></div>
...

書いてみたコード

まず、自分の知識で書いてみました。

product.js
varid_name=$('.products');for(vari=0;i<$(id_name).length;i++){varid_sp=id_name.eq(i).attr('class').split("")[1];varid=id_sp.substring(id_sp.indexOf("_")+1,id_sp.length);(function(i){$.ajax({url:id+'.html',type:'GET',dataType:'html',}).done(function(data){$('.'+id_sp).html($(data).find('.p_cate').text());})})(i);}

実行結果

index.html
<ul><liclass="products pid_111"><p>商品名①</p><pclass="cate"></p></li><liclass="products pid_222"><p>商品名②</p><pclass="cate"></p></li><liclass="products pid_333"><p>商品名③</p><pclass="cate">カテゴリ③</p></li></ul>

最後の商品にだけカテゴリが入ってしまいます。

コンソール実行

product.js
....done(function(data){$('.'+id_sp).html($(data).find('.p_cate').text());console.log(data+':'+id_sp);})/*
=> 結果
p_333:カテゴリ①
p_333:カテゴリ②
p_333:カテゴリ③
*/

ajax自体は正常に動いている様子です。
ループも商品の個数分回っています。
しかし、商品IDがすべて最後の商品のIDになっています。

考察

ajax処理が完了する前にループが回りきってしまうため
商品IDを取得するタイミングと商品カテゴリを取得するタイミングにずれがある?

追記

@netebakari様にコメントを頂き
そもそも「クロージャの性質」によるものだとわかりました。
(コメント参照)

forループ内の1つ目の変数
var id_name = $('.products');
constletで宣言することで
forループ内でも変数(定数)を維持することができて
適応した商品IDを取得することができました。

動くコード

product.js
varid_name=$('.products');for(vari=0;i<$(id_name).length;i++){constid_sp=id_name.eq(i).attr('class').split("")[1];// var→constへ変更varid=id_sp.substring(id_sp.indexOf("_")+1,id_sp.length);$.ajax({url:id+'.html',type:'GET',dataType:'html',}).done(function(data){$('.'+id_sp).html($(data).find('.p_cate').text());});}

コンソールを実行

product.js
....done(function(data){$('.'+id_sp).html($(data).find('.p_cate').text());console.log(data+':'+id_sp);})/*
=> 結果
p_111:カテゴリ①
p_222:カテゴリ②
p_333:カテゴリ③
*/

今度こそ、想定の動きをしてくれました!

対策を調べる

グーグル先生に聞いてみたところ、大きく3つの対応策がありました。

ajaxを非同期にする

こちらは特に変化なしです。
これが原因ではないようです。

無名関数で囲う

すでに囲っていました。
これも原因ではないようです。

取得した値を配列に含んでeachで吐き出し

もう、これしかありません。
頑張って書いてみます。

product.js
varid_name=$('.products');vararr=[];// <- 追加for(vari=0;i<$(id_name).length;i++){varid_sp=id_name.eq(i).attr('class').split("")[1];varid=id_sp.substring(id_sp.indexOf("_")+1,id_sp.length);(function(i){$.ajax({url:id+'.html',type:'GET',dataType:'html',}).done(function(data){varcate=$(data).find('.p_cate').text();// <- 変更arr.push({p_id:id_sp,cate_name:cate});})})(i);$.each(arr,function(index,value){// <- 追加varfind='.'+value.p_idvarcate_in=value.cate_name$(find).html(cate_in);});}

コンソール実行

product.js
...$.each(arr,function(index,value){varfind='.'+value.p_idvarcate_in=value.cate_name$(find).html(cate_in);console.log(find+':'+cate_in);});/*
=> 結果
p_111:カテゴリ①
p_222:カテゴリ②
p_333:カテゴリ③
*/

うまく回っています!
商品IDとカテゴリが適合しています!

実行

index.html
<ul><liclass="products pid_111"><p>商品名①</p><pclass="cate">カテゴリ①</p></li><liclass="products pid_222"><p>商品名②</p><pclass="cate">カテゴリ②</p></li><liclass="products pid_333"><p>商品名③</p><pclass="cate">カテゴリ③</p></li></ul>

しっかりと表示されました!
一安心です…笑

まとめ

一見単純な動きでも、ループや非同期通信が絡み合うと想定した動きにならないということを体感できました。
取得したデータを配列に格納して吐き出す、という考え方も知ることができました。
思考の幅がぐっと広がりました!

初学者が手探りで書いたコードなので、間違っている可能性も高いです。
もっと合理的な書き方をご存知の方は是非コメントを下さい!

Java を使ってEclipseのコンソールにカレンダーを表示させてみた。

あくまでメモ用として。
・cal.set(int year,int month-1,1)するときに月が「0から11」⇒「1月から12月」にセットされることを忘れがちだった。
・cal.getActualMaximum(Calendar.DATE)でxxxx年xx月は何日あるのかを知ることができる。
・曜日と日付は配列を使って格納した。

image.png

・カレンダーを作って出力するクラス
 
package foo;
import java.util.Calendar;
public class Calmaker {

public void Makecal(int year,int month){
Calendar cal = Calendar.getInstance();
//int weekday = cal.get(Calendar.DAY_OF_WEEK);
//System.out.println(weekday);

int days[][] = new int[6][7];

System.out.println("日 月 火 水 木 金 土");
int row = 0;

//一日から最終日までループ
for (int i = 1; i <= cal.getActualMaximum(Calendar.DATE); i++) {
    //日付けをオブジェクトに設定
    cal.clear();
    cal.set(year, month-1, i); //月は0から11で指定されているので、-1してあげる。
    int weekday = cal.get(Calendar.DAY_OF_WEEK);

    //日付を拡張
    days[row][weekday-1] = i ;
    //System.out.println(days[row][weekday-1]);

    if(weekday % 7 ==0) {
        row ++;
    }
}

//出力
    for (int k = 0; k < 6; k++) {
        for (int j = 0; j < 7; j++) {

            String res = String.valueOf(days[k][j]);

            if(days[k][j]==0) {
                System.out.print("   ");
            }else if(days[k][j] < 10) {
                    System.out.print(" " + res + " ");

            }else if(days[k][j] >= 10) {
                res = res+" ";
                System.out.print(res);
            }
            if(j == 6) {
                System.out.println("\r\n");
            }
        }
    }
}

}
・メインクラス

package foo;

import java.util.Scanner;

public class Makecal {

public static void main(String[] args) {
    // TODO 自動生成されたメソッド・スタブ
    System.out.println("西暦を4桁で入力してください");
    Scanner sc1 = new Scanner(System.in);
    System.out.println("月を1から12で入力してください");
    Scanner sc2 = new Scanner(System.in);

    int year  = sc1.nextInt();
    int month = sc2.nextInt();
    System.out.println("西暦"+year+"年" + month +"月");
    System.out.println("");

    Calmaker clm = new Calmaker();
    clm.Makecal(year,month);
}

}

image.png

終わり。

JavaScript 配列

目次

-配列の作り方
-配列の要素にアクセスする
-配列の要素を変更する
-配列の全ての要素に対して何らかの処理を行う
-配列に処理を行ったあとで結果を別の配列として取得する
-配列の要素のうち条件に合うものだけを抽出して別の配列として取得する

配列の作り方

 [] (大括弧)の中にそれぞれの値を , (カンマ)区切りで与えてあげる。
 たとえば、複数のスコアがあったとして、それをscores という定数名で全ての値を配列で管理したかった場合下記のようになる。

  // const score1 = 60;
  // const score2 = 80;
  // const score3 = 40;

const scores = [60, 80, 40];

配列の要素にアクセスする

配列ではインデックスは 1 番からではなくて 0 番から始まる。

const scores = [60, 80, 40];

上記の場合インデックス 0 番目が 60 、 1 番目が 80 、 2 番目が 40 となる。
2 番目のスコアだけを表示したかった場合

console.log(scores[1]);

とすると 80 が表示される。

3 つ目のスコア 40 点を 45 点に変更する場合
scores[2] = 45;

scores のインデックス 2 番目を修正したいので scores[2] に対して新しい値を代入してあげる。

scores は const で定義した定数だが、配列の要素への代入はできる。

配列の要素数を取得する方法

◯length を使う

console.log(scores.length);

とすると要素の数を表示できる。
今 scores に入っている要素の数は 3 つなので 3 が表示される。

配列の要素を変更する

下記の配列を使って説明していく。

const scores = [60, 80, 40];
配列の先頭に要素を追加

◯unshift() を使う
※ () の中に追加したい要素を , 区切りでいくつでも書いていくことができる
例 30点と65点を追加したかった場合

const scores = [60, 80, 40];
scores.unshift(30, 65);
//30, 65, 60, 80, 40
配列の末尾に要素を追加

◯push() を使う
※ () の中に追加したい要素を , 区切りでいくつでも書いていくことができる
例 30点と65点を追加したかった場合

const scores = [60, 80, 40];
scores.push(30, 65);
//60, 80, 40, 30, 65
配列の先頭から要素を削除

◯shift() を使う
※1 つずつしか要素を削除できない

const scores = [60, 80, 40];
scores.shift();
//80, 40
配列の末尾から削除

◯pop() を使う
※1 つずつしか要素を削除できない

const scores = [60, 80, 40];
scores.pop();
//60, 80
配列の途中の要素を操作

◯splice() を使う
・ splice(変化が開始する位置、削除数)
・ splice(変化が開始する位置、削除数、追加する要素、... )

下記の場合 変化が開始する位置 0番目が60 , 1番目が80 , 2番目が40

const scores = [60, 80, 40];
scores.splice(1, 1);
//60, 40
const scores = [60, 80, 40];
scores.splice(1, 2);
//60
const scores = [60, 80, 40];
scores.splice(1, 0, 55, 65);
//60, 55, 65, 80, 40
const scores = [60, 80, 40];
scores.splice(1, 1, 55, 65);
//60, 55, 65, 40
配列の中に別の配列を展開

◯... スプレット構文を使う

const otherScores = [10, 20];
const scores = [60, 80, 40, ...otherScores];
//60, 80, 40, 10, 20
配列の値を別々の定数にしたい

◯分割代入を使う

  const scores = [60, 80, 40];
  const [a, b, c] = scores;
  console.log(a); // 60
  console.log(b); // 80
  console.log(c); // 40
配列の値を定数と配列に分ける

◯... スプレット構文を使う
◯分割代入を使う

例 配列の値で定数に入れたいのは最初の1つだけで、あとの2つは配列のままにする

const scores = [60, 80, 40];
const [a, ...others] = scores;
console.log(a); //60
console.log(others); //[80, 40]
値の交換

◯分割代入を使う
例 xの値と、yの値を入れ替える

  let x = 100;
  let y = 0;
  [x, y] = [y, x];
  console.log(x); // 0
  console.log(y); // 100

配列の全ての要素に対して何らかの処理を行う

◯for を使う
◯length を使う

例 コンソールに全ての要素を表示する

const scores = [60, 80, 40];
for (let i = 0; i < scores.length; i++) {
  console.log(`${scores[i]}`);
}

配列の全ての要素に対して何らかの処理を行う

◯forEach() を使う

例 コンソールに全ての要素を表示する

const scores = [60, 80, 40];
scores.forEach((score) => {
  console.log(`${score}`);
});

配列に処理を行ったあとで結果を別の配列として取得する

◯map() を使う
例 それぞれを5点ずつ増やした結果を配列で取得する

const scores = [60, 80, 40];
const updateScores = scores.map(score => score + 5);
console.log(updateScores); // [65, 85, 45]

配列の要素のうち条件に合うものだけを抽出して別の配列として取得する

◯filter() を使う
例 80点以上を抽出し、goodScoresという配列として取得する

const scores = [60, 80, 40, 90, 100];
const goodScores = scores.filter(score => score >= 80 );
console.log(goodScores); // [80, 90, 100]

[PHP] 複数のキーが配列に存在するか or 必須キー群を網羅しているか確認する

受け取った配列が必須のキーを網羅しているか確認したい。

「php 複数キー 配列 確認」で Qiita 記事をググっても、タイトルからわかるピンポイントの記事がなかったので、自分のググラビリティとして。

TL; DR

必須キーの配列を用意し、それを array_flip()でキーと値をフリップ(反転)したのち、array_diff_key()でキーの差で比べる。

if(array_diff_key(array_flip($keys_required),$data)){echo'網羅していません';}
  • 必須キー以外は認めない場合は TS; DR の末尾参照

TS; DR

<?php// 必須のキーの配列$keys_required=['a','b','c',];// 受け取った配列$data=['a'=>'hoge','b'=>'fuga','c'=>'piyo','e'=>'mogera',];echoarray_diff_key(array_flip($keys_required),$data)?'必須キーを網羅していません':'必須キーを網羅しています',PHP_EOL;// 結果: 必須キーを網羅しています
いろんなパターン
<?php// 必須キー$keys_required=['a','b','c',];/* 必須キーが足りない */$data=['a'=>'hoge','b'=>'fuga',];echoarray_diff_key(array_flip($keys_required),$data)?'網羅していません':'網羅しています',PHP_EOL;//結果: 網羅していません/* 必須キーとピッタリ */$data=['a'=>'hoge','b'=>'fuga','c'=>'piyo',];echoarray_diff_key(array_flip($keys_required),$data)?'網羅していません':'網羅しています',PHP_EOL;// 結果: 網羅しています/* 必須キーとピッタリだが順番が違う */$data=['c'=>'piyo','b'=>'fuga','a'=>'hoge',];echoarray_diff_key(array_flip($keys_required),$data)?'網羅していません':'網羅しています',PHP_EOL;// 結果: 網羅しています/* 必須キーとピッタリだが値が null */$data=['a'=>'hoge','b'=>'fuga','c'=>null,];echoarray_diff_key(array_flip($keys_required),$data)?'網羅していません':'網羅しています',PHP_EOL;// 結果: 網羅しています/* 必須キーは網羅しているが余計なキーも含む */$data=['a'=>'hoge','b'=>'fuga','c'=>'piyo','d'=>'mogera',];echoarray_diff_key(array_flip($keys_required),$data)?'網羅していません':'網羅しています',PHP_EOL;// 結果: 網羅しています/* 必須キーは網羅していないが、要素数は同じ */$data=['a'=>'hoge','b'=>'fuga','e'=>'mogera',];echoarray_diff_key(array_flip($keys_required),$data)?'網羅していません':'網羅しています',PHP_EOL;// 結果: 網羅していません/* 必須キーは網羅しているが、値が null */$data=['a'=>'hoge','b'=>'fuga','c'=>null,// null であってもキーがあればおk'd'=>'piyo',];echoarray_diff_key(array_flip($keys_required),$data)?'網羅していません':'網羅しています',PHP_EOL;// 結果: 網羅しています

必須キー以外は認めない

array_diff_key($a, $b)array_diff_key($b, $a)の差を array_merge()する。

// 必須のキーの配列$keys_required=['a','b','c',];// 受け取った配列$data=['a'=>'hoge','b'=>'fuga','c'=>null,];$a=array_flip($keys_required);$b=$data;if(array_merge(array_diff_key($a,$b),array_diff_key($b,$a))){echo'網羅していません';}
<?php/* 必須キーが完全一致 */$data=['a'=>'hoge','b'=>'fuga','c'=>null,];$keys_required_flipped=array_flip($keys_required);echoarray_merge(array_diff_key($keys_required_flipped,$data),array_diff_key($data,$keys_required_flipped))?'網羅していません':'網羅しています',PHP_EOL;// 結果: 網羅しています/* 必須キーが足りない */$data=['a'=>'hoge','b'=>'fuga',];$keys_required_flipped=array_flip($keys_required);echoarray_merge(array_diff_key($keys_required_flipped,$data),array_diff_key($data,$keys_required_flipped))?'網羅していません':'網羅しています',PHP_EOL;// 結果: 網羅していません/* 必須キーは網羅しているが多い */$data=['a'=>'hoge','b'=>'fuga','c'=>null,// null'd'=>'piyo',];$keys_required_flipped=array_flip($keys_required);echoarray_merge(array_diff_key($keys_required_flipped,$data),array_diff_key($data,$keys_required_flipped))?'網羅していません':'網羅しています',PHP_EOL;// 結果: 網羅していません

Rails で使える Ruby 配列メソッド (each以外)

この記事で扱うRuby配列メソッド

1.selectメソッド
2.rejectメソッド
3.keep_ifメソッド
4.delete_ifメソッド
5.findメソッド
6.mapメソッド

Rubyバージョン: 2.5.1 で動作確認

書いたきっかけ

今まで配列メソッドといえばeach メソッドくらいしか使ったことありませんでした。(筆者はプログラミングを本格的に学習し始めて2ヶ月程度の初心者です。)
Rails で個人アプリを開発中、「DBテーブルから取得した配列データをうまい具合に加工して必要な要素だけ表示したいけど、うまくできない」っていうことが多々。
解決のために色々と調べる中で「こんなに便利なメソッドがあったのか!」と非常に勉強になったので、 知識の定着兼、備忘録として書こうと思います。

どなたかの参考になれば幸いです。

eachメソッドは使いこなせている、というくらいのレベル感の方を対象として書いています。

参考にしたもの

伊藤淳一さんの 「プロを目指す人のためのRuby入門」

伊藤さんのQiita記事、書籍等非常にわかりやすく、いつもお世話になっています。

今回動作確認用サンプルとして用意した配列

users=[{name: '松田',score: 80},{name: '大塚',score: 60},{name: '小池',score: 90},{name: '井上',score: 40},{name: '松本',score: 50},{name: '藤沢',score: 85}]

以下で一つ一つメソッドの動作を確認しつつ解説していきます。

1.selectメソッド

selectメソッドは配列の各要素に対してブロック内で指定した処理を行う。その戻り値がtrueの要素だけを集めた配列を返す。

実際に動作を確認

select.rb
# scoreが70以上のuserだけにしたい!users=[{name: '松田',score: 80},{name: '大塚',score: 60},{name: '小池',score: 90},{name: '井上',score: 40},{name: '松本',score: 50},{name: '藤沢',score: 85}]# selectを用いてscoreが70以上のuserのみを集めるgoukaku_users=users.select{|user|user[:score]>=70}# 新しく作成した変数の中身を確認pgoukaku_users# =>[{:name=>"松田", :score=>80}, {:name=>"小池", :score=>90}, {:name=>"藤沢", :score=>85}]# score70以上のuserだけの配列を作成できた!

※この記事ではブロックの中では簡単な処理しか行わないので
do...end{ }を用いる記法1行で書いています。
蛇足ですが、変数名などわかりずらかったらすみません。

2.rejectメソッド

rejectメソッドselectメソッドとは反対で、ブロック内での処理の戻り値がfalseの要素だけを集めた配列返すメソッド。

実際に動作を確認

reject.rb
# scoreが70以下のuserだけにしたい!users=[{name: '松田',score: 80},{name: '大塚',score: 60},{name: '小池',score: 90},{name: '井上',score: 40},{name: '松本',score: 50},{name: '藤沢',score: 85}]# rejectメソッドでscoreが70以下のuserのみを集めるfugoukaku_users=users.reject{|user|user[:score]>70}# 新しく作成した変数の中身を確認pfugoukaku_users# => [{:name=>"大塚", :score=>60}, {:name=>"井上", :score=>40}, {:name=>"松本", :score=>50}]# score70以下のuserだけの配列を作成できた!

3.keep_ifメソッド

keep_ifメソッドも配列の各要素を順番に取り出してブロック内で指定した処理を行う。その結果、戻り値がtrueであればブロックに渡した要素を配列に残し、戻り値がfalseであれば配列から削除する。

実際に動作を確認

keep_if.rb
# scoreが70以上のuserだけにしたい!users=[{name: '松田',score: 80},{name: '大塚',score: 60},{name: '小池',score: 90},{name: '井上',score: 40},{name: '松本',score: 50},{name: '藤沢',score: 85}]# keep_ifメソッドでscoreが70以上のuserのみに加工する# scoreが70より小さいusersの要素は削除users.keep_if{|user|user[:score]>=70}# keep_ifメソッド後pusers# => [{:name=>"松田", :score=>80}, {:name=>"小池", :score=>90}, {:name=>"藤沢", :score=>85}]# 元の配列usersをscore70以上のuserだけの配列にできた!

4.delete_ifメソッド

delete_ifメソッドも配列の各要素を順番に取り出してブロック内で指定した処理を行う。その結果、戻り値がtrueであればブロックに渡した要素を元の配列から削除する。戻り値がfalseであれば配列に残す。

実際に動作を確認

delete_if.rb
# scoreが70以下のuserだけにしたい!users=[{name: '松田',score: 80},{name: '大塚',score: 60},{name: '小池',score: 90},{name: '井上',score: 40},{name: '松本',score: 50},{name: '藤沢',score: 85}]# delete_ifメソッドでscoreが70以下のuserのみに加工する# scoreが70より大きいusersの要素は削除users.delete_if{|user|user[:score]>70}# delete_ifメソッド後pusers# => [{:name=>"大塚", :score=>60}, {:name=>"井上", :score=>40}, {:name=>"松本", :score=>50}]# 元の配列usersをscore70以下のuserだけの配列にできた!

keep_ifdelete_ifメソッドは 1,2で説明したselectメソッドrejectメソッドと同じ!?
と思ったのですが、比較すると

select / reject

元の配列はそのままに、条件に当てはまった要素を集めて全く新しい配列を返す。そのため再利用する際は変数へ代入する。

delete_if / keep_if

元の配列から条件に当てはまった要素を削除する。つまり元の配列の形自体を変えている。そのため、元の変数名で再利用が可能。

似ているのですがこんな違いがあるようです。

5.findメソッド

findメソッドはブロックで指定した処理の戻り値がtrueになる最初の要素を返す。

実際に動作を確認

find.rb
# 配列usersのscoreが70以上の最初のuserを取得したい!users=[{name: '松田',score: 80},{name: '大塚',score: 60},{name: '小池',score: 90},{name: '井上',score: 40},{name: '松本',score: 50},{name: '藤沢',score: 85}]# findメソッドでscoreが70以上の最初のuserを取得goukaku_user=users.find{|user|user[:score]>=70}# 新しく作成した変数の中身を確認pgoukaku_user# => {:name=>"松田", :score=>80}# 配列のscore70以上の最初のuserを取得できた!

念のため、ブロック内 score の条件を 70 → 85 に変えてみて再度検証

find.rb
# 配列usersのscoreが85以上の最初のuserを取得したい!users=[{name: '松田',score: 80},{name: '大塚',score: 60},{name: '小池',score: 90},{name: '井上',score: 40},{name: '松本',score: 50},{name: '藤沢',score: 85}]# findメソッドでscoreが85以上の最初のuserを取得goukaku_user=users.find{|user|user[:score]>=85}# 新しく作成した変数の中身を確認pgoukaku_user# => {:name=>"小池", :score=>90}# 配列のscore85以上の最初のuserを取得できた!

6.mapメソッド

mapメソッドは各要素に対してブロック内で指定した処理の結果、戻り値を新しい配列にして返す。

実際に動作を確認

map.rb
# 配列usersのnameに「さん」を付けたい!users=[{name: '松田',score: 80},{name: '大塚',score: 60},{name: '小池',score: 90},{name: '井上',score: 40},{name: '松本',score: 50},{name: '藤沢',score: 85}]san_users=users.map{|user|user[:name]+'さん'}# 新しく作成した変数の中身を確認psan_users# => ["松田さん", "大塚さん", "小池さん", "井上さん", "松本さん", "藤沢さん"]

あれ? ちょっと期待した動作と違った。
mapメソッドは今まで説明してきた配列メソッドとは少し異なる挙動をするよう。

具体的には、
元の配列の要素を評価して、要素を変形させて値を返す訳ではなく、処理結果の返り値をそのまま一つずつ配列に格納し、新しく作成した配列を返している。

javaScriptの mapメソッドを知っている方なら、理解しやすいかもしれないです。

map.rb
# なので元の形を維持しつつ、ハッシュ形式で返したい場合は次のように書くsan_users=users.map{|user|{name: user[:name]+'さん',score: user[:score]}}psan_users# => [{:name=>"松田さん", :score=>80}, {:name=>"大塚さん", :score=>60}, {:name=>"小池さん", :score=>90}, {:name=>"井上さん", :score=>40}, {:name=>"松本さん", :score=>50}, {:name=>"藤沢さん", :score=>85}]# nameに「さん」をつけた配列を作成できた!

最後に

記事を読んでいただきありがとうございます。

Rails で開発をしていて、メソッドを知らないと知っているではできることの範囲やコードリファクタリングなどに大きく影響してくることを痛感。改めて基礎の大切さを感じているところです。

内容に誤りなどあれば、気軽にご指摘、ご意見いただけると嬉しいです!

【C言語】配列を引数として渡すことの考察(2次元配列まで)

はじめに

C言語の関数で、配列を引数として渡す方法については、数多のサイトで紹介されています。
ただし、2次元配列については方法が複数あり、うまく使い分けることが必要となりますので、そのあたりの考察を含めて書いておきます。

わかりやすいように図表をつけて、1次元配列から2次元配列まで順を追って書いていきます。
使用している環境はmacで、コンパイラはgccです。

1次元配列(数値型)を引数として渡す

まずは、基本形として、数値型(int)の1次元配列についてです。
C言語では、配列そのものを引数として渡せないので、ポインタを引数として渡します。
具体的には、次のソースコード中、main関数4行目にあるnum_arr(num, numlen);のところとなります。

●ソースコード

num_arr.c
#include <stdio.h>
voidnum_arr(int*num,intnumlen){for(inti=0;i<numlen;i++){printf("%d ",num[i]);}}intmain(void){intnum[]={13,5,33,69,37,14,98,23};intnumlen=sizeof(num)/sizeof(int);num_arr(num,numlen);return0;}

●ターミナルで実行

$ gcc num_arr.c 
$ ./a.out
13 5 33 69 37 14 98 23

ここで注意を要するのは、引数として渡すのは、配列の先頭ポインタを示すnumだけではなく、配列の要素数numlenも渡していることです。
これは、ポインタnumが、次のイメージ図のように配列num[]の先頭アドレスの情報(1500)しか持っていないためです。

●イメージ図
スクリーンショット 2020-06-21 17.52.00.png

ソースコード中のnum_arr関数側では、配列の先頭アドレスの情報(num = 1500)だけを受け取っても、それだけでは配列としての要素数がわからないということになります。
これを補うために、配列の要素数numlenが引数として必要になります。

なお、配列num[]はint型で指定しているため、配列のアドレスは、1500、1504、1508というように4バイトごとに確保されています(環境によっては、int型が4バイトでない場合もあります。)。
ポインタを1つインクリメントするたびに、4バイトずつアドレスが進むことになります。

1次元配列(文字型)を引数として渡す

次に、char型の配列(文字列)を引数として渡す場合です。
C言語における文字列は、基本的にはchar型の配列になるので、これも文字列(配列)そのものを渡すことはできず、文字列の先頭ポインタを引数として渡すことになります。
具体的には、次のソースコード中、main関数3行目にあるstr_arr(str);のところとなります。

●ソースコード

chr_arr.c
#include <stdio.h>
voidstr_arr(char*str){printf("%s\n",str);}intmain(void){char*str="HELLO";str_arr(str);return0;}

●ターミナルで実行

$ gcc chr_arr.c 
$ ./a.out
HELLO

数値型と異なり、引数として渡すのは、配列の先頭ポインタを示すstrのみで足ります。
これは、文字列(char型配列)の末尾には、NULL文字'\0'があるため、受け取り側(str_arr関数)でも配列数(要素数)が簡単にわかるからです。

●イメージ図
スクリーンショット 2020-06-21 17.53.20.png

ソースコード中のstr_arr関数側では、配列の先頭アドレスの情報(str = 3300)だけを受け取れば、そこから末端NULL文字までを文字型配列として認識すればよいことになります。

2次元配列(数値型)を引数として渡す

(1) 2次元配列(数値型)の一般的な方法

次に、数値型(int型)の2次元配列についてです。
一般的には、次のようにすれば、引数に渡すことができます。

●ソースコード

num_arr2-1.c
#include <stdio.h>
voidnum_arr2(intnum[][5],intnumline,intnumlen){for(inti=0;i<numline;i++){for(intj=0;j<numlen;j++){printf("%d ",num[i][j]);}printf("\n");}}intmain(void){intnum[][5]={{32,4,78,34,64},{74,5,66,36,42},{56,13,55,3,81},{7,56,33,83,4},{32,85,50,24,39},{16,24,56,43,6},{75,35,27,34,83},{69,41,62,2,88}};intnumlen=5;intnumline=sizeof(num)/sizeof(int)/numlen;// つまり numline = 8 となるnum_arr2(num,numline,numlen);return0;}

●ターミナルで実行

$ gcc num_arr2-1.c 
$ ./a.out
32 4 78 34 64 
74 5 66 36 42 
56 13 55 3 81 
7 56 33 83 4 
32 85 50 24 39 
16 24 56 43 6 
75 35 27 34 83 
69 41 62 2 88 

ソースコード中、引数の受け取り側であるnum_arr2関数では、次のように第1引数で、int num[][5]という形式で、受け取る配列を指定しています。

sample.c
voidnum_arr2(intnum[][5],intnumline,intnumlen)

これは、各行ごとの要素数を指定しないと、プログラム上、2次元配列として認識されないためです。
2次元配列num[][]のデータは、次のような形で、格納されています。

● イメージ
スクリーンショット 2020-06-21 16.16.12.png
上記のように、各行のデータが全て繋がっており、データ上は2次元配列でも構造的には1次元配列と同じ形になっています。
そのため、num_arr2関数第1引数のint num[][5]は、見かけ上は、int **numのようなダブルポインタが渡されているように見えますが、実際は、int *numと同様のシングルポインタが渡されていることになります。

なお、1次元配列と同様の理由で、行数int numlineと列数int numlenは、別途に引数として渡す必要があります。

(2) 2次元配列(数値型)をシングルポインタで渡す方法

一般的な方法で配列を渡すと、あらかじめ要素数が固定されてしまい汎用性に乏しくなります。
そこで、あえてシングルポインタで配列を渡すと、次のとおりとなります。

●ソースコード

num_arr2-2.c
#include <stdio.h>
voidnum_arr2(int*num,intnumline,intnumlen){for(inti=0;i<numline;i++){for(intj=0;j<numlen;j++){printf("%d ",num[i*numlen+j]);}printf("\n");}}intmain(void){intnum[][5]={{32,4,78,34,64},{74,5,66,36,42},{56,13,55,3,81},{7,56,33,83,4},{32,85,50,24,39},{16,24,56,43,6},{75,35,27,34,83},{69,41,62,2,88}};intnumlen=5;intnumline=sizeof(num)/sizeof(int)/numlen;int*np=(int*)num;num_arr2(np,numline,numlen);return0;}

●ターミナルで実行

$ gcc num_arr2-2.c 
$ ./a.out
32 4 78 34 64 
74 5 66 36 42 
56 13 55 3 81 
7 56 33 83 4 
32 85 50 24 39 
16 24 56 43 6 
75 35 27 34 83 
69 41 62 2 88 

以上のようなソースコードを記載することで、シングルポインタで配列を渡すことができます。
ただし、int num[][5]というような、規則に則った記載をしていないため、プログラム上では2次元配列として認識されず、1次元配列として認識されます。
そのため、配列の取り出しにnum[i][j]という形は使えないので、次のような回りくどい方法で取り出しをしています(上のイメージ図を見れば、式の意味はわかると思います。)。

sample.c
printf("%d ",num[i*numlen+j]);

少々、問題は残りますが、受け取り側のnum_arr2関数で列数(要素数)を固定することはないので、汎用的に使用することができるようになります。

(3) 2次元配列(数値型)をダブルポインタで渡す方法

さて、次に、配列をダブルポインタで渡す方法です。
これをするには、配列の構造を、次のイメージ図のように変えてあげる必要があります(たぶん)。
スクリーンショット 2020-06-21 16.47.24.png
ダブルポインタが示すアドレス10は、シングルポインタの先頭アドレスとなりますす。
そして、このシングルポインタが示すアドレス100は、目的となるデータが格納されているアドレスとなります。
これに準じてソースコードを書き直すと、次のようになります。

●ソースコード

num_arr2-3.c
#include <stdio.h>
#include <stdlib.h>
voidnum_arr2(int**num,intnumline,intnumlen){for(inti=0;i<numline;i++){for(intj=0;j<numlen;j++){printf("%d ",num[i][j]);}printf("\n");}}intmain(void){intinput_num[][5]={{32,4,78,34,64},{74,5,66,36,42},{56,13,55,3,81},{7,56,33,83,4},{32,85,50,24,39},{16,24,56,43,6},{75,35,27,34,83},{69,41,62,2,88}};// ポインタを使用して2次元の構造にするintnumlen=5;intnumline=sizeof(input_num)/sizeof(int)/numlen;int**num=malloc(numline*sizeof(int*));for(inti=0;i<numline;i++){num[i]=malloc(numlen*sizeof(int));for(intj=0;j<numlen;j++){num[i][j]=input_num[i][j];}}// num_arr2関数の実行num_arr2(num,numline,numlen);// メモリの解放for(inti=0;i<numline;i++){free(num[i]);//各行のメモリを解放}free(num);return0;}

●ターミナルで実行

$ gcc num_arr2-3.c 
$ ./a.out
32 4 78 34 64 
74 5 66 36 42 
56 13 55 3 81 
7 56 33 83 4 
32 85 50 24 39 
16 24 56 43 6 
75 35 27 34 83 
69 41 62 2 88 

多少、無駄が生じてしまいますので、ここまでやる必要があるかは、目的によると思います。
ただ、こうすることで、(※個人的に)違和感なく汎用的に、2次元配列の受け渡しができるようになりました。

(4) 2次元配列(数値型)を簡単に渡す方法(C99に準拠している場合のみ)

2次元配列(数値型)の最後として、C99で使用可能な方法を書いておきます(※ご指摘を受けて一部修正しました)。
これは、「C言語の引数に多次元配列を渡す」という記事を元とさせていただきました。

●ソースコード

num_arr2-c99.c
#include <stdio.h>
voidnum_arr2(intnumline,intnumlen,intnum[numline][numlen]){for(inti=0;i<numline;i++){for(intj=0;j<numlen;j++){printf("%d ",num[i][j]);}printf("\n");}}intmain(void){intnum[][5]={{32,4,78,34,64},{74,5,66,36,42},{56,13,55,3,81},{7,56,33,83,4},{32,85,50,24,39},{16,24,56,43,6},{75,35,27,34,83},{69,41,62,2,88}};intnumlen=5;intnumline=sizeof(num)/sizeof(int)/numlen;num_arr2(numline,numlen,num);return0;}

(※実行結果は同じなので省略)

C99に準拠している環境であれば、これを使えば便利だと思います(私の環境では、gccでは動きますが、Visual Studioでは動きませんでした)。
なお、配列のポインタを渡す引数int num[numline][numlen]は最後(第3引数)にしないと読み込みができずエラーが起きるので注意してください。

2次元配列(文字型)を引数として渡す

文字配列(char型配列)についても、基本的には、数値型と同じ考え方で対応ができると思います。

参考として、実験的に各行のバイト数を可変長で取得した場合のソースコードを、下記に紹介しておきます。
細かいことは、こちらの記事「C言語におけるファイル情報の読み取りと文字型配列への格納」に書いてあります。
この元記事を書いた頃はまだビギナーだったので、輪を掛けて拙い記述となっている点はご容赦ください。

●ソースコード

chr_arr2.c
#include <stdio.h>
#include <stdlib.h>
intget_ftext(char**str,constchar*fname,intlines,int*len);intget_ftext_lines(constchar*fname);intget_ftext_len(int*len,constchar*fname);intmain(void){// ファイルの読み出し(get_ftext関数の実行まで)constchar*fname="file01.txt";// ファイル名を設定(ソースコードと同じフォルダに)intlines=get_ftext_lines(fname);// get_ftext_lines関数を用いて、ファイルの行数を取得int*len=(int*)malloc(lines*sizeof(int));// 変数lenは、各行のバイト数を格納するためのものget_ftext_len(len,fname);// get_ftext_len関数を用いて、変数lenに各行のバイト数を入れるchar**str=(char**)malloc(lines*sizeof(char*));for(inti=0;i<lines;i++){str[i]=(char*)malloc((len[i]+1)*sizeof(char));// 各行に文字列を格納するメモリを確保}get_ftext(str,fname,lines,len);// get_ftext関数を用いて、strの用意したメモリにファイルの文字列データを格納// 実行結果の確認printf("<strの格納データは以下のとおり>\n");for(inti=0;i<lines;i++){printf("%s",str[i]);}// メモリの解放for(inti=0;i<lines;i++){free(str[i]);// 各行のメモリを解放}free(str);free(len);return0;}// get_ftext関数(二次元配列を使ってファイル内の文字列データを行ごとに格納する関数)intget_ftext(char**str,constchar*fname,intlines,int*len){FILE*fp=fopen(fname,"rb");if(fp==NULL){printf("file open error!\n");return-1;}for(inti=0;i<lines&&fgets(str[i],len[i]+1,fp)!=NULL;i++){}fclose(fp);return0;}// get_ftext_lines関数(ファイルの行数を取得する関数)intget_ftext_lines(constchar*fname){FILE*fp=fopen(fname,"rb");intc;intlines=0;// 行をカウントする変数if(fp==NULL){printf("file open error!\n");return-1;}while(1){c=fgetc(fp);if(c=='\n'){// 改行があるたびに行をカウント(なお、fgetsの場合'\n'を改行として認識する)lines++;}elseif(c==EOF){// 最終行に改行がされていない場合も1行として拾うための処置lines++;break;}}fclose(fp);returnlines;}// get_ftext_len関数(ポインタ[len]にファイルの行ごとのバイト長を格納する)intget_ftext_len(int*len,constchar*fname){FILE*fp=fopen(fname,"rb");intc;inti=0;intbyt=0;//バイト数のカウント用intbyt_tmp=0;//変数bytの一時コピー用if(fp==NULL){printf("file open error!\n");return-1;}while(1){c=fgetc(fp);byt++;if(c=='\n'){len[i]=byt-byt_tmp;i++;byt_tmp=byt;//bytの数値をbyt_tmpにコピー}elseif(c==EOF){if((byt-byt_tmp)<=1){//最後が改行で終わっている場合len[i]=0;}else{len[i]=byt-byt_tmp;//最後が改行で終わっていない場合}break;}}fclose(fp);return0;}

2次元配列を渡しているのは、次の関数です。

sample.c
intget_ftext(char**str,constchar*fname,intlines,int*len);

このダブルポインタのstrに渡されるのは、ファイル内のテキストが入るだけのメモリを確保した空の領域(のポインタ)となります。

さいごに

数か月ぶりに、C言語を扱いました。
基本的なことを忘れがちなので、良いテーマがあれば少しずつ記事にまとめていこうと思っています。

配列について【 forEach .filter .map .sort 偏】

はじめに

今回は配列の要素を効率よく操作するためのループ処理について、アウトプットの為に書いて詳しく書いていきたいと思います。

配列要素を繰り返し処理する(forEach)

forEachは配列データに対してのみ実行することが可能で、各要素1つずつに任意の処理を実行させることができます。

基本的な構文と使い方

  • 一般的な使い方としては、配列.forEach( 処理 )のように配列データに対してforEachを実行します。
  • forEach文は、配列データの値1つずつに対してコールバック関数に記述した処理を実行できます。
  • コールバック関数は、いくつか引数を受けとることが可能で、これにより簡単な繰り返し処理を実現しています。

(書き方の例)

constarray=[配列データ];array.forEach(コールバック関数)

配列の中身を取得する

まず一つ目はvalueを使って、配列をループさせながら中身の要素を取得する書き方を説明します。

  • for文と違って、繰り返し回数やカウンタ変数などをまったく意識せずに扱えるので、非常にシンプルにコードが書けるのが特徴です。

(例)

constanimals=['イヌ','オオカミ','ハムスター','キリン','ネズミ'];animals.forEach(function(value){console.log(value);});

実行結果

イヌ 
オオカミ
ハムスター
キリン
ネズミ

このプログラムは、配列を使い、それぞれの名称をコンソールログで表示するという内容です。コールバック関数の引数としてvalueを設定していますが、この変数に配列の値となる名称が1つずつ代入されるようになります。

コールバック関数の使い方

コールバック関数の引数として3つの値を受け取ることができます。

1. value:配列データの値
2. index:配列のインデックス番号
3. array:現在処理している配列
があります。
例として、配列の元データをすべて2倍にして上書きするプログラムを書いてみます。

constnumbers=[1,2,3,4,5,6,7,8];numbers.forEach(function(value,index,array){array[index]=value*2;});console.log(numbers);

出力結果

[ 2,  4,  6,  8, 10, 12, 14, 16 ]

このプログラムは「value」を倍にしてから、「array」で、現在処理している配列の元データからarray[ index ]に格納し直しています。

forEach文ではbreakやcontinueは使えない!

forEachはfor文と違い、繰り返し処理を中断するbreakcontinue使えません
もし使った場合、繰り返し処理を中断しようとしますが利用できないため出力出来ずはエラーとなってしまいます。

配列要素を抽出する(filter)

次にfilterについて説明します

  • filter」は、特定の条件を与えて配列データを取得したい内容を「コールバック関数」に書きます。
  • またfilterのコールバック関数の引数もvalue(配列の値)index(配列のインデックス番号)array(現在処理) 】の3つがあります。
  • 任意のデータを抽出して新しい配列を生成することができます。

(例)

//5よりも小さい数値だけを抽出する場合constitems=[5,2,7,8,3,1,6,8,4];constresult=items.filter(function(value){returnvalue<5;})console.log(result);

出力結果

[2, 3, 1, 4]

結果を見るとさまざまな数値が格納されている配列の中から、5より小さい数値だけを新しい配列データとして出力することが出来ています。
文字列の場合も同じで、任意の文字列を抽出することができます。

処理した要素を新しい配列で作成する(map)

今度は、mapによる配列処理について見ていきます。

  • map関数は各要素に対して決まった繰り返し処理をして、新しい配列を生成するのに使える便利な関数です
  • mapforEachとよく似ていますが、最終的に新しい配列として生成してくれる点が大きく異なります
  • またコールバック関数の引数は上2つと同様、【 value(配列の値)index(配列のインデックス番号)array(現在処理) 】の3つがあります

(例)

//各要素を二乗した配列を作るconstlist=[1,2,3,4,5,6];constnewList=list.map(function(item){returnitem**2;});console.log(newList);

出力結果

[ 1, 4, 9, 16, 25, 36 ]

returnされたものが新しい配列の要素一つになっていることが分かります。

配列を並び替えする方法(sort)

  • 配列要素はsort()メソッドを利用することで、効率よく並び替えを行うことができます。
  • 文字列順や数字の大小などによる昇順・降順で対象の値を並び替えることができるようになるので、データの操作などでよく使われます。
  • 数値やオブジェクト構造などの場合だと、sort()をそのまま実行しても上手く行かないので比較関数(function compareFunc)を使う必要があります。
  • 「比較関数」は、2つの値を比較しながら1つずつ順番を入れ替えていく手法をとっています。

(例)

//1.文字の場合constarray=['c','a','b','d'];array.sort();console.log(array);//2.数字を降順でソートする方法functioncompareFunc(a,b){returnb-a;}constnumbers=[5,3,10,6,55];numbers.sort(compareFunc);console.log(numbers);

出力結果

//1.文字の場合
[ 'a', 'b', 'c', 'd' ]
//2.数字を降順でソートする方法
[ 55, 10, 6, 5, 3 ]

2.ではreturn b - aとすることで降順でソートすることができています。
昇順の場合はreturn a - bとすることができreturnの中身を変えることで応用させることもできます。
しかしsort()は元の配列データを変更してしまう破壊的メソッドでもあるので注意が必要です

最後に

今回はforEachfiltermapsortについてご説明しました。4つともうまく使い分けれたらとても便利だなと思いました!

参考リンク

JavaScript入門】sort()による配列・文字列・オブジェクトのソート方法
【JavaScript】map関数を用いたおしゃれな配列処理
JavaScript入門】配列の使い方と操作まとめ(初期化・追加・結合・検索・削除)

配列基本編(結合・検索・追加・削除)

はじめに

初めまして
今回は「配列」の使い方についてアウトプットするために詳しく書きたいと思います。

そもそも配列とは

簡潔にいうと配列とは、複数の値を1つのデータのように取り扱うことができる物です。
通常はconst text = 'こんにちは'のように文字列や数値などの値を1つの変数に格納していきます。しかし数が増えてくると1つ1つ変数名をつけるのが大変になってしまうような時に、配列データを作成することで大量の値を1つのデータのように扱えるわけです。

配列の使い方

配列を宣言する方法

JavaScriptで配列を使うためには2種類の方法があります。
1. 配列コンストラクタ「Array」を使う方法です。「配列コンストラクタ」を使う場合は、「new」を使ってArray()の引数へ格納したい文字列を設定します
2. 配列リテラル「[]」を使う方法です。「配列リテラル」を使う場合は、[ ]を使ってそのまま文字列を設定すればOKです!

具体例で説明します。

//①Arrayを使う例constarray1=newArray('イヌ','オオカミ','ハムスター');//②配列リテラルの例constarray2=['イヌ','オオカミ','ハムスター'];console.log(array1);//['イヌ', 'オオカミ', 'ハムスター']console.log(array2);//['イヌ', 'オオカミ', 'ハムスター']

どちらの方法でも同じ配列データが作成できているのを確認できますが、この2種類の方法はいくつか相違点があるのですが、最も違うのは1つだけの数値を格納しようとした時に発生します。例で紹介します。

vararray1=newArray(5);vararray2=[5];console.log(array1);//[empty × 5]console.log(array2);//[5]
  • どちらの宣言方法にも「5」という数値を設定していますが実行結果は異なっています。
  • 実は、配列コンストラクタで「new Array(5)」と記述すると「5つ分の配列要素を用意する」という意味になるのです。

JavaScriptで配列を宣言(作成)する時は、特に理由がない限り「配列リテラル」を使うようにしましょう

配列の要素を取得する方法

  • 取得したい配列変数の要素番号を[]で指定することで、その要素の中身を取得することができます。

  • 注意点として、要素番号(インデックス)は0番から始まるということです。
    (例)

lettextArr=['イヌ','オオカミ','ハムスター'];console.log(textArr[0]);//イヌ

配列の要素を検索する方法(indexOf)

配列内にある各要素のデータを簡単に検索できる方法が「indexOf()」メソッドがあるのでご紹介します。

  • 「indexOf()」は、指定した「値」が配列データに存在する場合にその場所を「インデックス番号」で取得できる機能を持っています。
  • 配列データに対して「indexOf('')」を記述することで、その文字列が存在するかを確認しています。

(例)

lettextArr=['イヌ','オオカミ','ハムスター'];letresult=textArr.indexOf('ハムスター');console.log(result);//2

配列の要素を追加する方法

先頭に追加(unshift)

先頭に追加する場合は.unshift('')で追加できます。
(例)

lettextarr=['イヌ','オオカミ','ハムスター'];textArr.unshift('キリン');console.log(textArr);//['キリン','イヌ', 'オオカミ', 'ハムスター']

末尾に追加(push)

末尾に追加する場合は.push('')で追加できます。
(例)

lettextArr=['イヌ','オオカミ','ハムスター'];textArr.push('キリン');console.log(textArr);//['イヌ', 'オオカミ', 'ハムスター','キリン']

指定した位置に追加 (splice)

spliceは文字を追加するでけではなく、指定した場所の文字を消すこともできます。
.splice(,,)で追加できます。
言葉では難しいので例で詳しく説明します。

//text2番目にヒヨコを挿入lettextArr=['イヌ','オオカミ','ハムスター','キリン','ネズミ'];textArr.splice(1,0,'ヒヨコ');console.log(textArr);//['イヌ','ヒヨコ' ,'オオカミ', 'ハムスター','キリン','ネズミ']//text3番目から2つ削除しヒヨコを挿入lettextArr=['イヌ','オオカミ','ハムスター','キリン','ネズミ'];textArr.splice(2,2,'ヒヨコ');console.log(textArr);//['イヌ', 'オオカミ', 'ヒヨコ', 'ネズミ' ]

要素の削除

配列の要素を削除する(delete)

delete([])と書いて削除できます。
(例)

//二番目を削除lettextArr=['イヌ','オオカミ','ハムスター'];deletetextArr[1]console.log(textArr);//['イヌ', 'ハムスター']

先頭を削除 (shift)

.shift()で削除できます。
(例)

lettextArr=['イヌ','オオカミ','ハムスター'];textArr.shift('');console.log(textArr);//['オオカミ', 'ハムスター']

末尾を削除する(pop)

.pop()で削除できます。
(例)

lettextArr=['イヌ','オオカミ','ハムスター'];textArr.pop('');console.log(textArr);//['イヌ', 'オオカミ']

指定した位置から削除(splice)

先ほど使ったspliceでできます。
.splice(,)と書いて削除できます。
(例)

//text2番目から3個削除lettextArr=['イヌ','オオカミ','ハムスター','キリン','ネズミ'];textArr.splice(1,3);console.log(textArr);//['イヌ','ネズミ']

配列の全要素を削除(length)

.length=0で削除できます。
(例)

lettextArr=['イヌ','オオカミ','ハムスター'];textArr.lengt=0;console.log(textArr);//[]

最後に

今回は配列の使い方、要素の検索、追加、削除についてお話ししました。
今回書けなかったその他の要素については次回かこうと思っています。
基本となる部分で私自身も書いていて為になったのでぜひ、使ってみてください。

参考リンク

JavaScriptの配列の使い方まとめ。要素の追加,結合,取得,削除。
【JavaScript】入門配列の使い方と操作まとめ(初期化・追加・結合・検索・削除)

[Swift]複数の要素を配列から削除する方法

背景

SwiftとFirebaseを使ってマッチングアプリを作っていたところ、不正ユーザーをブロックする機能を実装する必要が出てきました。

そして、タイトルにもあるように、「配列から複数の要素を削除する方法」が必要になったのでその方法を自分なりに考えてみました。

方針

array1からarray2と重複する要素を除去します。

実際の環境では、自分以外の全ユーザーのドキュメントIDをDBから取得して配列(array1)とし、ブロックしたユーザーのドキュメントIDをDBから取得して配列(array2)として、array1からarray2の要素を除去することで、ブロックしていないユーザーのドキュメントIDからなる配列を取得します。

具体的な方法

vararray1=["a","b","c","d","e","f"]vararray2=["a","d","e"]varduplicateIndexs:[Int]=[]varcount=0foriin0..<array1.count{ifarray2.contains(array1[i]){duplicateIndexs.append(i)}}print(duplicateIndexs)// [0, 3, 4]foriin0..<duplicateIndexs.count{array1.remove(at:duplicateIndexs[i]-count)count+=1}print(array1)// ["b", "c", "f"]

補足

duplicateIndexsとか、countとかなんかごちゃごちゃやっているなという感じですが、
単に以下のコードだとIndex out of rangeのエラーが出ます。

vararray1=["a","b","c","d","e","f"]vararray2=["a","d","e"]foriin0..<array1.count{ifarray2.contains(array1[i]){array1.remove(at:i)}}print(array1)

removeで除去しつつfor文を回しているので、array1が短くなっていき(要素が減っていき)、array1[i]の部分がIndex out of rangeになるんですね。

このエラーを避けるために、duplicateIndexsとか、countとか用意してなんとか目的の、array2の要素を除去した新たな配列を取得することに成功しました。

「もっと他にいい実装方法あるよ」という意見やアドバイスをいただけると幸いです!

【VBA】使い回せる配列操作用関数

VBAの配列操作アレコレ

定期的にVBAでツールの作成依頼が来るのですが、大体は外部ファイルを読み込んで処理してExcelワークシートなりCSVに出力するような機能のものが多いです。
そこで頻出するのが配列に関する処理。
配列を使用すると、しない場合に比べて「応答なし」になる確率が下がり、処理時間も短くなることが多いです。
だけどVBAの配列は、本職Java屋の私にはちょっと癖がある!ので、汎用的な関数を作ってはため込んで再利用しています。
この記事では、よく使う配列操作用の自作関数をまとめました。
更新や追加等メンテは、随時行っていく予定です。

配列の要素数を取得

VBAには、配列の要素数を取得する関数が存在しません。
UBound(配列)関数は、配列の末尾の添え字(インデックス)を取得する関数であり、要素数の取得ではありません。
例えば、arr(0 to 3)の配列に対してUBound(arr)の結果は「3」です。
ですが、添え字は「0、1、2、3」で、要素数は「4」になります。
arr(1 to 3)であればUBound(arr)の結果は「3」で要素数と一致しますが、先端の添え字をLBound(arr)で取得して確認する必要があります。
また、LBound(arr)UBound(arr)は、宣言したての動的配列(Dim arr())に対しては、実行時エラー「9」が発生します。
上記仕様により配列の要素数の取得には色々考慮が必要となるので、このような関数を作成しています。

' 配列の要素数を取得(要素数0対応)FunctionLength(ByValarrAsVariant)AsLongOnErrorGoToEMPTY_ARRIfIsArray(arr)ThenLength=UBound(arr)+(1-LBound(arr))ElseLength=-1EndIfExitFunctionEMPTY_ARR:IfErr.Number=9Then' インデックス範囲外エラーLength=0EndIfEndFunction

戻り値「-1」のケースについて

動的配列の宣言は、Dim arr()でもDim arr(またはDim arr As Variant)でも可能です。
ポイントはDim arrで、宣言した後にRedimで配列型として初期化が可能です。
この場合、初期化前は配列ではありません。配列としてLBound(arr)等の処理は実行時エラー「13」(型不正)となります。
そのため、配列でない(初期化前の)引数の場合は、「-1」を返しています。(必要に応じて戻り値を「0」にしても)
引数に初期化前の配列変数を渡さないことを保証できるのであれば不要な分岐ですが、汎用さを確保するためには必要です。
Function Length(Byval arr() As Variant) As Longのように配列型で宣言できればいいんですけども。

動的配列の最後に要素を追加

動的配列はRedimで要素数を変更できます。
無条件でRedim arr(0)等できない場合、初めて配列に要素を追加するタイミングで要素数を増やす必要があります。
新規に配列を作り直すわけではないので、FunctionではなくSubにし、操作したい配列はByRefで参照渡しにしています。
※この記事で紹介しているLength関数を組み合わせて使っています。

' 動的配列の最後に要素を追加' arr:追加先の配列' value:追加する要素SubAddLast(ByRefarrAsVariant,ByValvalueAsVariant)IfIsEmpty(arr)OrLength(arr)<1Then' 初期化前または要素数=0の場合ReDimarr(0)ElseReDimPreservearr(UBound(arr)+1)EndIfarr(UBound(arr))=valueEndSub

初期化前、または要素数が0の場合について

空の配列変数に対して、LBound関数およびUBound関数は、実行時エラー「9」が発生します。なので空判定には使用できません。
Dim arr()で宣言している配列変数は、Redimで初期化前でもIsEmpty(arr)の結果がFalseになります。
そのため、IsEmpty(arr)だけではなく、Sgn(arr) = 0または(Not Not arr) = 0でも判定が必要です。
これで解決!と思いうじゃないですか。
でも、要素数が1以上の()なし(型指定なしまたはVariant型)で宣言した引数を渡すと、実行時エラー「13」が発生します…

そんなわけで上で説明しているLength関数を使用しているのですが、ループ内で大量に呼ぶと何回かに1回4秒くらいかかって激重・激遅になります。
(大量に呼び出して処理時間をDebug.Printしてみたら、0秒 Or 3.9秒がランダムに出力された…何でばらつく??)
ですので、実際使用する場合は、呼び出し元の配列変数の宣言時の型と引数の配列の空判定方法の組み合わせを、以下のどちらかのパターンに寄せたほうがいいです。

  1. 配列変数をDim arr As Variantのように()なしのVariant型で宣言し、空判定をIsEmpty(arr)で行う
  2. 配列変数をDim arr() As 型のように()ありで宣言し、空判定をSgn(arr) = 0または(Not Not arr) = 0で行う

私は大体1ですね。IsEmpty(arr)は見た目で何やってるかわかりやすいので。
しかし何つー仕様だMicr○soft。Variant型の配列としての空判定は必ずOn Error GoTo ~使えってか…()なしならRedimで配列として初期化できないようにしろよお…
可変長配列は、セルに直接代入できないけどCollectionオブジェクト、またはVB.NetのArrayListオブジェクト使うって手もあるんですけどもね。これらはNewしたらAddで追加できるので、もろもろの考慮不要です。

ジャグ配列(入れ子の配列)を二次元配列に変換

ループでセルひとつひとつに出力すると重いので、セルに出力する処理は、配列を作って1回でセルに出力することが多いです。
そんな時に使用する多次元配列。VBAのいう多次元配列は、Java等の多次元配列と違い、ジャグ配列とは異なります。(ジャグ配列だとセルにそのまま出力不可)
ただ、VBAの多次元配列って、可変長なのは末尾の次元(ジャグ配列でいう入れ子の最下層)のみなんですよ。
CSVを読み込んで処理して必要な行だけセルに出す、なんてありがちな処理は、予め出力行数がわかってないと配列を宣言できないし、かといってCSVがクソデカファイルだとFileSystemObjectを使用して行数だけを取得するにしても、処理時間がかかる。
なので、可読性と速さのバランスを取ると、ジャグ配列で処理後、セルへの出力時に二次元配列に変換するのが一番わかりやすいなって気がします。そんな時に使用するのがこの関数です。

' ジャグ配列を二次元配列に変換FunctionConv2DemintionalArray(ByValsrcArrAsVariant)AsVariantDimresultAsVariant:ReDimresult(UBound(srcArr),UBound(srcArr(0)))DimiAsLong:i=0DimrowAsVariant:ForEachrowInsrcArrDimjAsLong:Forj=0ToUBound(srcArr(0))result(i,j)=row(j)Nexti=i+1NextConv2DemintionalArray=resultEndFunction

VBA書くなら配列の処理は頻出だと思うので、どうにか覚えていくしかないんですよね…

[JavaScript]配列を極めたい奴はこれを見ろ

配列とは何か?

複数の値をまるで1つのデータのように取り扱うことができるようになるもの。
配列データを作成することで大量の値を1つのデータのように扱えるわけです。
よく言う例えとしては、データを入れる箱のようなものと覚えておけばいいでしょう。

配列の作成方法

[]のなかにデータを入れていく。

constarray=['リンゴ','バナナ','イチゴ'];

配列の要素を取得する方法

配列の0番目から数えて取得したい値の番号を指定する。

constarray=['リンゴ','バナナ','イチゴ'];console.log(array[0]);  // =>リンゴ

配列の要素を検索する方法

「indexOf()」は、指定した「値」が配列データに存在する場合にその場所を「インデックス番号」で取得できる機能を持っています。なのでこちらのメソッドを使用します。

constarray=['リンゴ','バナナ','イチゴ'];constresult=items.indexOf('リンゴ');console.log(result);  // =>0

配列要素の追加

pushメソッドを使用することで配列の最後に要素を追加できる。

constarray=['リンゴ','バナナ','イチゴ'];array.push('メロン');console.log(array);  // =>['リンゴ', 'バナナ', 'イチゴ', 'メロン']

配列の要素を削除する方法

deleteメソッドを使用することで、指定した要素を削除できます。
※削除した部分は空になります。

constarray=['リンゴ','バナナ','イチゴ'];deletearray[1];console.log(array);  // =>['リンゴ', '', 'イチゴ']

配列から要素を削除・追加して組み替える

splice()メソッドを使用することで、配列の要素を自由に削除・追加してデータを組み替えることが出来ます。

constarray=['リンゴ','バナナ','イチゴ'];// const newArray = array(追加・削除する位置, 削除する要素の数, 追加する要素...);constnewArray=array.splice(1,2,'メロン','マンゴー');console.log(newArray);  // =>['リンゴ', 'メロン', 'マンゴー']

配列を連結・結合する方法

join()メソッドを使用することで、配列の各要素を連結・結合して「文字列(String)」として出力することができる。
※引数に何も指定しなければカンマ区切りになります。

constitems=[2020,6,29];constresult=items.join('/');console.log(result);  // =>2020/6/29

配列を分割する方法

slice()を使用することで、指定した配列のインデックス番号を境目に末尾まで分割して、新しい配列データを取得することができます。
※第一引数=開始位置、第二引数=終了位置 と言う指定方法も可能。

constarray=['リンゴ','バナナ','イチゴ'];constresult=array.slice(2);console.log(newArray);  // =>['イチゴ']

配列要素を抽出する方法

filter()メソッドは、任意の条件に合致する配列要素だけを抽出して新しい配列データを作成することができます。

constitems=[55,20,70,80,30,15,65,85,40];constresult=items.filter(function(value){//50よりも小さい数値だけを抽出returnvalue<50;})console.log(result);  // =>[55, 70, 80, 65, 85]

処理した要素を新しい配列で作成する方法

map()メソッドを使用することで、処理した後に新たに配列を作成してくれます。

constitems=[1,2,3,4,5];constresult=items.map(function(value){//配列の各要素を2倍にするreturnvalue*2;});console.log(result);  // =>[2, 4, 6, 8, 10]

配列要素を繰り返し処理する方法

forEach()メソッドを使用することで、配列データに対してのみ実行することが可能で、各要素1つずつに任意の処理を実行させることができます。

letitems=[1,2,3,4,5];letresult=items.forEach(function(value){//配列の各要素を2倍にするreturnvalue*2;});// =>2// =>4// =>6// =>8// =>10

配列をコピーする方法

concat()メソッドを使用することで、指定した配列を対象の配列に連結出来ます。
空の配列に連結させることでコピーしているような形になります。

constarray=['リンゴ','バナナ','イチゴ'];constnewArray=array.concat();console.log(array);// => ['リンゴ', 'バナナ', 'イチゴ']console.log(newArray);// => ['リンゴ', 'バナナ', 'イチゴ']

配列の要素数を調べる方法

lengthメソッドを使用することで配列内の要素数を調べることが出来ます。

constarray=['リンゴ','バナナ','イチゴ'];array.length;

配列をソートする方法

sort()メソッドを利用することで、効率よくソート(並び替え)を行うことができます。
※文字列の場合はそのままで良いが、数値やオブジェクトの場合は比較関数を使います。

//文字列の場合conststr=['sa','mu','ra','i'];str.sort();console.log(str);// => ["i", "mu", "ra", "sa"]//数値の場合functioncompareFunc(a,b){returna-b;}varnum=[5,3,10,6,55];num.sort(compareFunc);console.log(num);// => [55, 10, 6, 5, 3]//オブジェクトの場合constlists=[{name:'メロン',price:500},{name:'バナナ',price:100},{name:'スイカ',price:280},{name:'イチゴ',price:300}];lists.sort(function(a,b){if(a.name>b.name){return1;}else{return-1;}})console.log(lists);// =>[{name:"イチゴ",price:300}{name:"スイカ",price:280}{name:"バナナ",price:100}{name:"メロン",price:500}]

スプレッド構文

配列やオブジェクトの要素を文字通り展開する構文のこと。

constfoo=[1,2];// 要素を追加して新しい配列を生成constbaz=[...foo,3,4];// => [1, 2, 3, 4]

レスト構文

複数の要素を集約して 1 つのオブジェクトにまとめることが出来ます。
関数の引数や分割代入での不特定多数の値を配列として受け取る際に利用します。

constarr=[1,2,3,4,5];const[a,b,...c]=arr;console.log(a,b,c);// =>  1  2  [3, 4, 5]

まとめ

JavaScriptで使うであろう配列の知識をまとめました。
これらを使いこなせば基礎は身につくんではないかと思います!

本棚で考えるJavascriptの配列操作

javascriptのオブジェクトを本棚に見立てて
色々操作してみる。

constbooks={novel:{name:['火花','赤毛のアン','星の王子様']},comic:{name:['鬼滅の刃','ONE PIECE','キングダム']},business:{name:['7つの週間','サピエンス全史','自助論']},philosophy:{name:['方法序説','死に至る病','ニコマコス倫理学']},biography:{name:['フランクリン自伝','果てなき野望','マイ・ストーリー']},favorite:{name:['FACTFULLNESS','蹴りたい背中','恋は雨上がりのように']},}console.log(books);

Q1

新しく進撃の巨人を買ったので本棚に追加する

  • comicに進撃の巨人を追加(先頭に)

A

books.comic.name.unshift('進撃の巨人');

ちなみに末尾に追加するにはpush

Q2

7つの習慣を読みたくなってきた

  • 7つの習慣を取り出す
  • 「7つの習慣を読み終わりました」と出力する
  • businessの棚の一番最後に戻す。

A

constchoose=books.business.name.shift();console.log(choose+'を読み終わりました');books.business.name.push(choose);console.log(books);

7つの習慣を読み終わりましたと出力された後に
business.nameの配列の最後に7つの週間が追加されていればOK

Q3

「赤毛のアン」がとても面白かったのでお気に入りの棚に追加しようと思ったが、お気に入りの棚がもういっぱいで本が入らない!

仕方ないので、「恋は雨上がりのように」は漫画の棚に移動する

  • 赤毛のアンを取り出す
  • 恋は雨上がりのようにを取り出す
  • 赤毛のアンをfavoriteに追加
  • 恋は雨上がりのようにcomicに追加

A

constread=books.novel.name.splice(1,1);console.log(read+'を読みました')//読んだ後にパンパンだと気づくconstmove=books.favorite.name.pop();books.comic.name.push(move);books.favorite.name.push(read);console.log(books);
  • novelが2つになって
  • comicに恋は雨上がりのようにが追加されて
  • favoriteに赤毛のアンが追加

されてればOK

[Swift5]paizaでよく使う文法メモ(初心者向け)

背景

普段Swift使ってアプリを開発していますが、
「paizaをSランクにしておくといいかも」との助言もあり、ついこの間paizaをやりはじめてみました。

普段使わない文法で慣れていなかったり、そもそもコードのバージョンが古いサイトもあったので、自分用に「paizaでよく使う文法」をまとめました。
参考にどうぞ。

よく使うコード

標準入力

//1行取得 例:1letinput_line=readLine()!//複数行 letinput_line1=readLine()!letinput_line2=readLine()!letinput_line3=readLine()!//複数行varstrs=[String]()for_in0..<8{strs.append(readLine()!)}//複数行whileletline=readLine(){// 入力された行に対する処理}

※7/4追記
コメント欄で以下の方法を教えていただきました。ありがとうございます。

//複数行取得lets=AnyIterator{readLine()}.joined()//配列で取得(くそ便利)letarr=Array(AnyIterator{readLine()})

文字列の処理

//例: 1 3 5 7 → ["1","3","5","7"]letstrs=input_line.split(separator:" ")//例: 16357 → ["1","6","3","5","7"]letstrs=input_line.map{String($0)}//例: ["1","3","5","7"] → [1,3,5,7]letnumbers=strs.map({Int($0)!})

補足:「標準入力〜文字列の処理」をこんな関数にしてやると便利かも

funcreadInts()->[Int]{returnreadLine()!.split(separator:" ").map{Int($0)!}}

さて、続き。

lets="ぎょうざたべたい"//前から◯文字取得s.prefix(3)//ぎょう//後ろから◯文字取得s.suffix(2)//たい//◯文字切り捨てs.dropLast(2)//ぎょうざたべ//文字列切り出し m文字目〜n文字目leti1=s.index(s.startIndex,offsetBy:3)leti2=s.index(s.startIndex,offsetBy:6)s[i1...i2]//ざたべたs[i1..<i2]//ざたべ
//文字列結合lets1="はら"lets2="へった"s1+s2//はらへったletarray=["はら","へった"]array.joined()//はらへったarray.joined(separator:" ")//はら へったarray.joined(separator:",")//はら,へった
//文字列に文字が含まれるかlets="はろーSwift"ifs.contains("は"){print("OK")}
//文字列の一部を置き換えvars="はじめましてこんにちは"ifletrange=s.range(of:"こんにちは"){s.replaceSubrange(range,with:"さようなら")//はじめましてさようなら}
//大文字・小文字leta="swift".uppercased()letb="SWIFT".lowercased()
//ある文字列をn個繰り返すlets=String(repeating:"AB",count:5)// "ABABABABAB"

配列初期化

vara=[String]()varb=[[String]]()b.append(["はじ","めまして"])

配列並び替え

vararray=[5,2,6,7,1]array.sort{$0<$1}//昇順 [1,2,5,6,7]array.sort{$0>$1}//降順 [7,6,5,2,1]array.reversed()//逆順 [1,7,6,2,5]

・sort()は配列を書き換える
・sorted()は配列を書き換えない

配列絞り込み

//例:3で割り切れるもののみからなる配列にletarray=[5,2,6,3,1]array.filter({$0%3==0})//[6,3]

配列結合

letarr1=[5,2,6]letarr2=[3,1]arr1+arr2//[5,2,6,3,1]//配列に要素を追加letarr=["a","b","c","e"]arr.insert("d",at:3)//["a", "b", "c", "d", "e"]//配列末尾に要素を追加arr.append("←")//["a", "b", "c", "e", "f"]

配列の要素の合計値

letarray=[1,2,3,4,5]letresult=array.reduce(0){$0+$1}//15

このコードだとreduce()の良さが出てない気もしますが。。。

配列の全要素が条件を満たすかどうか

//全ての要素が偶数かどうかletvalues=[2,4,6,8]//contains()letisAllEven=!values.contains{!($0%2==0)}//allSatisfy()letisAllEven=values.allSatisfy{$0%2==0}

allSatisfy()の方が可読性が高いですね。Swift 4.2でSequenceに追加されたメソッドです。

配列のindexを取得

letscores=[8,6,1,62,90]//enumerated()for(index,score)inscores.enumerated(){print("\(index): \(score)")}//index(of: )ifletindex=scores.index(of:90){print("index: \(index)")}//index(where: )ifletindex=scores.index(where:{$0==90}){print("index: \(index)")}

enumerated()ですが、インデックスと要素のタプルを返します。

配列をユニークに

letarray=[3,4,2,2,1,5,1,3,4]letresult=array.reduce([],{$0.contains($1)?$0:$0+[$1]})// [1, 2, 3, 4, 5]

三項演算子を使って書いています。
[]に要素をわんこそばのように追加していくイメージです

絶対値

leta=abs(-6)//6

累乗

leta=pow(5.0,3)

◯進数

leta=44letstr2=String(num,radix:2)letstr16=String(num,radix:16)//2進数を10進数にletint=Int("110110",radix:2)

最大、最小

max(1,5,2)//5min(1,5,2)//2leth=[65.7,64.3,61.1,67.5,58.5,60.3,64.9]letmax=h.max()//67.5letmin=h.min()//58.5

倍数

//2の倍数か?leta=18a.isMultiple(of:2)//true

小数点以下切り捨て・切り上げ・四捨五入

leta=3.1floor(a)//切り捨て 3.0ceil(a)//切り上げ 4.0round(a)//四捨五入 3.0//小数点をずらして整数値にletb=a*10floor(b)/10//切り捨て 3ceil(b)/10//切り上げ 4round(b)/10//四捨五入 3

0埋めする format指定

letnum1=String(format:"%04d",9)//0009letnum2=String(format:"%0.3f",0.2)// 0.200

終わりに

「他にもこれよく使うよ」という文法があったらコメントで教えてくれると喜びます(笑)

一緒にpaizaランクSを目指しましょう!

Ruby 二次元配列

二次元配列とは?

2次元配列とは、プログラムで利用される配列において、配列の中に配列が入っている配列のことである。

ソースコード

fruits_price=[["apple",[200,250,220]],["orange",[100,120,80]],["melon",[1200,1500]]]# 配列Key 合計額算出fruits_price.eachdo|fruit|sum=0fruit[1].eachdo|price|sum+=priceendputs"#{fruit[0]}の合計金額は#{sum}円です"end

結果

appleの合計金額は670円です
orangeの合計金額は300円です
melonの合計金額は2700円です

説明

keyにフルーツの名前、valueにそのフルーツの値段が配列で複数

[["apple", [200, 250, 220]]

Ruby each文

オブジェクト.each do |変数|
  実行する処理1
  実行する処理2
end

こちらで要素を一つづつ取り出しています。

fruits_price.eachdo|fruit|sum=0fruit[1].eachdo|price|sum+=priceend

ここで、配列の中の価格が入っている配列に対して要素を取り出し、
合計(sum)に格納します。

これで完成です。

Browsing Latest Articles All 136 Live