Quantcast
Channel: 配列タグが付けられた新着記事 - Qiita
Viewing all 821 articles
Browse latest View live

C++でstd::vectorと静的配列[]を組み合わせて深さ優先探索(DFS)

$
0
0

動的配列std::vectorと静的配列[]

C++には、処理の過程で動的に要素数を追加・削除できるstd::vectorと、
変数の宣言時に要素数を静的に決めてしまう配列[]があります。
この記事では、C++で頻繁に使われる両者の長所を組み合わせた深さ優先探索の実装を紹介します。

変数の宣言

まず基本的なことですが、std::vectorと静的配列[]はそれぞれ以下のように宣言することができます。

int a[10];  //変数型 変数名[要素数];
std::vector<int> v; //vector<変数型> 変数名; 要素数指定なし(=要素数0)
std::vector<int> vv(10); //vector<変数型> 変数名(要素数);要素数指定あり

std::vectorを宣言するときはstd::vectorと記述する必要があるのに対し、
静的配列を宣言するときは変数名の後ろに[要素数]を記述すれば良い点に注意してください。
また、std::vectorの宣言時には要素数を指定する(2行目)ことも、要素数を指定しない(3行目)こともできます。
vector宣言時において要素数を指定しないと要素数0のvectorが生成されるため、要素数0指定でvector宣言することと等価です。

std::vectorと静的配列[]を組み合わせて深さ優先探索(DFS)

入力

入力として下記の木構造情報が標準入力から与えられるとします。

木構造データ形式
n:ノード数
a_1 b_1  :ノード間のリンク情報(無向)
:
a_n-1 b_n-1

例えば、以下のような形式です。

標準入力
6
1 2
1 3
2 4
3 6
2 5

変数宣言

std::vectorと静的配列[]を組み合わせることで、以下のような変数を宣言することができます。

std::vector<int> v[n]; //静的配列[]の中にvectorが格納されている

この宣言をすることで要素数nの静的配列の中に動的配列(宣言時は要素数0)のvectorが格納されているような変数になります。
ノード数は与えられているが各ノードの子ノードの数はわからない、といったケースではこのような配列が有効です。
この後に出てくる木構造データの学習、探索時にその効果がよくわかると思います。

木構造の学習

下記のコードでは、標準入力で与えられたリンク情報を上記で宣言した配列vecに格納していきます。

  for(int i=0;i<n-1;++i){
    cin >> a >> b;
    --a;--b;
    vec[a].push_back(b);
    vec[b].push_back(a);
  }

変数vec自体は要素数nで初期化しているので、はじめから0~n-1番目の任意の要素にアクセスすることができます。
そして、その各要素は動的配列std::vector(初期要素数0)であるため、push_backすることで動的に要素を追加していくことができます。
上記の例では、ノードaとノードb間にリンクがあることを配列vecに保存しています。
(今回は無向グラフを考えているため、a→bとb→aの双方向のリンク情報を格納しています。)
一般的に、木構造データでは一つのノードが二つ以上の子を持つことがあるため、単純な一次元配列ではこのようなデータ構造を保持するのは難しいと思います。

深さ優先探索(DFS)

では、上記で学習した木構造を使って深さ優先探索を実装してみましょう。

DFS
void dfs(int v, int p){  //v:探索中のノード p:親ノード
  for(auto u : vec[v]){  //vの子ノードでfor文を回す
    if(u != p){          //親ノードは探索しない
    //ここに処理を記述する。
    dfs(u,v);            //探索中ノードを親ノードとし、子ノードを探索する(深さ優先)
    }
  }
}

上記の関数では、リンク情報を学習した配列vecを基に、あるノードvの子ノードvec[v]を順に探索していきます。
main関数中では以下のように呼び出せば良いでしょう。

main
dfs(0,-1);//ノード0が一番親である場合。親=-1で親ノードが存在しないことを示す

使用例(AtCoder Beginner Contest 138: D-Ki)

上記の配列を活用することで、例えば以下のような問題をシンプルに解くことができます。
AtCoder Beginner Contest 138: D - Ki

解答
#include<iostream>
#include<vector>

using namespace std;

vector<int> vec[200002];  //vectorと静的配列の組み合わせ
//N<=200000なのでこれくらい要素数があれば足りる
vector<int> ans;

void dfs(int v, int p){   //v:探索中のノード p:親ノード
  for(auto u : vec[v]){   //vの子ノードでfor文を回す
    if(u == p)continue;   //親ノードは探索しない
    ans[u] += ans[v];
    dfs(u,v);             //探索中ノードを親ノードとし、子ノードを探索する(深さ優先)
  }
}

int main(){

  int a,b,i,n,p,q,x;

  cin >> n >> q;
  ans.resize(n);
  for(i=0;i<n-1;++i){
    cin >> a >> b;
    --a;--b;
    vec[a].push_back(b);   //a→bのリンク情報を格納
    vec[b].push_back(a);   //b→aのリンク情報を格納
  }
  for(i=0;i<q;++i){
    cin >> p >> x;
    --p;
    ans[p] += x;
  }

  dfs(0,-1);                //深さ優先探索

  for(i=0;i<n;++i)cout << ans[i] << endl;

  return 0;
}

終わりに

C++のstd::vectorや配列[]を使いこなせるようになると実装力はぐっと上がります。
ぜひ上記の他にも色々な配列の使い方を試してみてください。
内容に不備や不明点などありましたらコメントでお願いします。


【駆逐してやる...】JavaScriptでExcelブックを配列化【1ファイル残らず!】

$
0
0

久々の業務で役立つ(かも?)シリーズです。

「受け取ったデータが.xlsxだった...」
「社内システムが.xlsxしか吐き出してくれない...」
.xlsxをこねくり回すと全身に拒絶反応が...

そのような絶望とも今日でおさらば!

js-xlsx

.xlsx以外にも.xls.odsなど、大抵の表計算ファイルをJavaScriptから扱えます。

もちろんShift-JISにも対応しており、なんとNode/Browserどちらでも使えます。

まさに救世主!

導入

Browser
<script src="https://cdn.jsdelivr.net/npm/xlsx@latest/dist/xlsx.full.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xlsx@latest/dist/cpexcel.js"></script>

グローバル領域にXLSXオブジェクトが展開されます。
cpexcel.jsは追加の文字コードセットで、これが無いとShift-JISを認識できません。

Node
const XLSX = require("xlsx");

こちらは単純明快。
文字コードセットは全て内包されているので問題ありません。

コード

JavaScript
function parseSheet(blob){
    return new Promise((res, rej)=>{
        const fr = new FileReader();
        fr.addEventListener("load", () => res(fr.result));
        fr.addEventListener("error", () => rej(fr.error));
        fr.readAsArrayBuffer(blob);
    }).then((bin)=>{
        const book = XLSX.read(bin, {
            type: "array",
            codepage: 932
        });

        const sheets = book.SheetNames.map((name)=>{
            const sheet = XLSX.utils.sheet_to_csv(book.Sheets[name], {
                FS: "\u001F"
            }).split("\n").map((row)=>{
                return row.split("\u001F").map((field)=>{
                    if(/^$/.test(field)){
                        return null;
                    }
                    else if(/^true$/i.test(field)){
                        return true;
                    }
                    else if(/^false$/i.test(field)){
                        return false;
                    }
                    else if(/^((|-)((\d{1,3},){0,}\d{3}|\d+)|0(X|x)[0-9a-fA-F]+|0(B|b)[0-1]+)$/.test(field)){
                        const s = field.replace(/,/g, "");
                        const n = Number(s);
                        return Number.isSafeInteger(n) ? n : BigInt(s);
                    }
                    else{
                        return String(field);
                    }
                });
            });

            return {
                [name]: sheet
            };
        });

        return Object.assign(...sheets);
    });
}

各シートを一旦CSVにしてから、各フィールドの型判定を行い二次元配列化しています。

空白セルはnullとなります。
Numberは、桁区切りでカンマが入っていたり16進や2進の表記でも問題ありません。
値が巨大な場合はBigIntとなります。
それ以外の文字列はStringです。

出力されるオブジェクトは、下記のようなデータ構造です。

DataStructure
{
    "シート名": [/*行番号*/][/*列番号*/],
    ...
}

使う

HowToUse.js
const file; // 任意の表計算ファイル

const parsed = parseSheet(file);

console.log(parsed["シート1"][0][0]); // シート1のA1セルに相当

行/列の番号が0スタートなところさえ気を付ければ、特に難しい操作はありません。

これで正規表現での総当りも、条件付きSUMも、フォームへの自動入力も思いのまま!

おわりに

Excelのない朝は
今よりずっと素晴らしくて
すべての歯車が噛み合った
きっとそんな世界だ

配列の要素同士を足す

$
0
0

要素同士を足すと調べても配列の要素の追加が多く出てしまったので忘れないようにメモ

lines=[]

while line=gets do
    lines<<line.chomp
end

m_g,m_s,m_b=["Gold","Silver","Bronze"]

n_g,n_s,n_b=lines

puts "#{m_g} #{n_g}"
puts "#{m_s} #{n_s}"
puts "#{m_b} #{n_b}"

を簡潔に書きたいと思い調べてみると
Arrayクラスのメソッドzipを見つけ使ってみました。

lines=[]

while line=gets do
    lines<<line.chomp
end

medals=["Gold","Silver","Bronze"]

sums = medals.zip(lines).map{|m,n| "#{m} #{n}"}

sums.each do |n|
puts n
end

長さはあまり変わりませんでしたが物が多くなった時にputsをたくさん
書く必要がないのでいいかな、、、と思います!

zipのるりま

色々な言語で配列そのものを順序比較

$
0
0

次のJavaScriptコードの実行結果はどうなるでしょうか?

[1,11,3] > [1,2,4]

答えはfalse。配列が文字列化されて比較される。JSでの順序比較は ToPrimitive という処理を行い、その戻り値を使って比較するらしい。

あの分かりにくいECMAScript仕様書を読むと、順序比較時の ToPrimitive は以下の流れになっているようだ。

  1. @@toPrimitive メソッドを取得。取得できれば呼び出して、その結果を返す。
  2. valueOf メソッドを呼ぶ。戻り値がObjectでなければ、その結果を返す
  3. toStringメソッドを呼び、その結果を返す

ArrayにはSymbol.toPrimitiveがなく、valueOfはArray自身を返す。ArrayはObjectなので、toStringが呼ばれて文字列となるようだ。

で、他の言語ではどうなるのか気になったので色々な言語で配列の大小比較をしてみた。

Ruby

[1, 11, 3] > [1, 2, 4]

例外がスローされる。Rubyは演算子もメソッドであり、Arrayは > メソッドを実装していないのでNoMethodError。

Java

public class Hoge {
  public static void main(String[] args) {
    int[] x = new int[1];
    int[] y = new int[2];
    System.out.println(x > y);
  }
}

コンパイルエラー、エラー: 二項演算子'>'のオペランド型が不正です

Golang

package main

import "fmt"

func main() {
    a := []int{1, 11, 3}
    b := []int{1, 2, 4}
    fmt.Println(a > b)
}

コンパイルエラー、invalid operation: a > b (operator > not defined on slice)

Swift

let a = [1, 11, 3]
let b = [1, 2, 4]

print(a > b)

コンパイルエラー、error: binary operator '>' cannot be applied to two '[Int]' operands

Rust

fn main() {
    let a = [1, 11, 3];
    let b = [1, 2, 4];
    println!("{}", a > b);
}
$ ./main
true

先頭の要素から順番に比較しているようだ。

RustではOrdトレイトを実装すると大小比較が可能になる。スライスもOrdを実装している。2つのスライスの長さが異なるとコンパイルエラーになるが、可変長のvecは長さが違っても比較可能であった。

Python

>>> [1, 11, 3] > [1, 2, 4]
True

Rustと同じっぽい

PHP

<?php
echo [1,11,3] >[1,2,4];
?>
1

boolean型ないんでしたね。おそらくRust、Pythonと同じ結果でしょう

C++

#include <iostream>

int main() {
  int a[3] = {1, 2, 3};
  int b[3] = {1, 11, 3};
  std::cout << (a > b) << std::endl;
}

警告が出ます。aとbの定義位置を入れ替えると結果が変わります。これはポインタのアドレスの比較のようです。

まとめ

言語 結果
JavaScript toStringして比較
Ruby 実行時エラー
Java コンパイルエラー
Go コンパイルエラー
Swift コンパイルエラー
Rust 要素の先頭から比較
Python 要素の先頭から比較
PHP 要素の先頭から比較
C++ 不明

Groovyで配列からn個づつ取り出す

$
0
0

コード

def domain=['out','open','vid','uuu','rrrr','tttt','kkk','rrr','ppp','rfde','wsaq']

def splitCount = 5

for (i in 0..Math.ceil(domain.size() / splitCount) -1 ) {  

    //開始index
    def firstIndex = i == 0 ? i : i * splitCount

    //終了index
    def lastIndex = (firstIndex + splitCount - 1) < domain.size() - 1 ?  (firstIndex + splitCount - 1) : domain.size() - 1

    print "start:" + firstIndex + "\n"
    print "end:" + lastIndex + "\n"

    print "get list:" + domain[firstIndex..lastIndex] + "\n"

}

結果

start:0
end:4
get list:[out, open, vid, uuu, rrrr]
start:5
end:9
get list:[tttt, kkk, rrr, ppp, rfde]
start:10
end:10
get list:[wsaq]

【React】Reactで便利な配列操作まとめ

$
0
0

React で便利な配列操作まとめ

エンジニアの t-77 です。
Reactで配列を使用する際に、知っていると便利な操作のまとめです。

配列をコピー: slice

配列名.slice()で、配列をコピーできます。
()に値を指定すれば、コピーする範囲を指定できます。

const animalsList = [dog, tiger, lion, wolf, bison, fox, rabbit];

// 全てコピー
const animalsList2 = animalsList.slice();
// 3番目の要素から全てコピー
const animalsList3 = animalsList.slice(2);
// 1番目の要素から5番目の要素までコピー
const animalsList4 = animalsList.slice(0, 4);

console.log(animalsList);
console.log(animalsList2);
console.log(animalsList3);
console.log(animalsList4);
console
[dog, tiger, lion, wolf, bison, fox, rabbit]
[dog, tiger, lion, wolf, bison, fox, rabbit]
[lion, wolf, bison, fox, rabbit]
[dog, tiger, lion, wolf, bison]

配列の先頭に追加: unshift

配列名.unshift(要素)で、配列の先頭に要素を追加できます。
()に値を複数入れれば、複数の要素を追加できます。

const animalsList = [dog, tiger, lion, wolf, bison, fox, rabbit];
const animalsList2 = animalsList.slice();
animalsList2.unshift('cat');

console.log(animalsList);
console.log(animalsList2);

animalsList2.unshift('mouse', 'elephant');
console.log(animalsList2);
console
[dog, tiger, lion, wolf, bison, fox, rabbit]
[cat, dog, tiger, lion, wolf, bison, fox, rabbit]
[mouse, elephant, cat, dog, tiger, lion, wolf, bison, fox, rabbit]

配列の末尾に追加: push

配列名.push(要素)で、配列の末尾に要素を追加できます。
()に値を複数入れれば、複数の要素を追加できます。

const animalsList = [dog, tiger, lion, wolf, bison, fox, rabbit];
const animalsList2 = animalsList.slice();
animalsList2.push('cat');

console.log(animalsList);
console.log(animalsList2);

animalsList2.push('mouse', 'elephant');
console.log(animalsList2);
console
[dog, tiger, lion, wolf, bison, fox, rabbit]
[dog, tiger, lion, wolf, bison, fox, rabbit, cat]
[dog, tiger, lion, wolf, bison, fox, rabbit, cat, mouse, elephant]

配列の先頭を削除: shift

配列名.shift()で、配列の先頭の要素を削除できます。

const animalsList = [dog, tiger, lion, wolf, bison, fox, rabbit];
const animalsList2 = animalsList.slice();
animalsList2.shift();

console.log(animalsList);
console.log(animalsList2);
console
[dog, tiger, lion, wolf, bison, fox, rabbit]
[tiger, lion, wolf, bison, fox, rabbit]

配列の末尾を削除: pop

配列名.pop()で、配列の末尾の要素を削除できます。

const animalsList = [dog, tiger, lion, wolf, bison, fox, rabbit];
const animalsList2 = animalsList.slice();
animalsList2.pop();

console.log(animalsList);
console.log(animalsList2);
console
[dog, tiger, lion, wolf, bison, fox, rabbit]
[dog, tiger, lion, wolf, bison, fox]

要素数を確認: length

配列名.lengthで、配列の要素数が確認できます。

const animalsList = [dog, tiger, lion, wolf, bison, fox, rabbit];

console.log(animalsList.length);
console
7

まとめ

覚えておくと便利な配列操作一覧

  • 要素数を確認: 配列名.length
  • 配列をコピー: 配列名.slice()
  • 配列の先頭に追加: 配列名.unshift(要素)
  • 配列の末尾に追加: 配列名.push(要素)
  • 配列の先頭を削除: 配列名.shift()
  • 配列の末尾を削除: 配列名.pop()

今回は、以上になります。ご覧いただき、ありがとうございました!

Pythonで配列の参照取り出し

$
0
0

introduction

競技プログラミングの問題を解いていた際に用いた方法について書いていきます。
配列の任意の部分を参照する処理について記述します。

誤っているところがあればコメント等よろしくお願いします。

配列の準備

下記で任意の2次元配列をnumpyを用いて作成しました。

import numpy as np
array=np.arange(16).reshape((4,4)) #データを準備

>>>array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])

配列の任意部分を参照する処理

例えば下記に示すような任意の赤丸で囲った配列を参照したいとき

スクリーンショット 2019-08-31 15.57.17.png

pythonで実装すると以下のようにできると考えました。

div_array=[]
for i in range(1,3):
    tep_array=[]
    for j in range(1,3):
        tep_array.append(array[i][j])
    div_array.append(tep_array)    
print(div_array)    

#div_array=[[5, 6], [9, 10]]

ただ、この方法だと記述量も多く、変数をたくさん置く必要があって読みにくいです。

それを解決するために、ndarray型配列であれば、スライスを用いることで実装できます。
(ただしpython のlistでlist[1:3,1:3]と指定するとエラーがおきます。)

div_array=array[1:3,1:3]

>>>div_array([[ 5,  6],
       [ 9, 10]])

まず、左側の1:3で行方向の参照を行い、
[ 4, 5, 6, 7] と[ 8, 9, 10, 11]の行を参照します。

次に、右側の1:3で列方向の参照ができます。
よって、[5,6] と [9,10] が参照できます。

ただ、この場合、コピーではなく、参照ですので、div_arrayを変更すると参照元のarrayも変更してしまいます。

参照元の配列を変更したくない場合は、

div_array=array[1:3,1:3].copy()

と記述することで解決できます。

追記:list内listをprint()するときの豆知識

話が少し逸れますが、list内の要素を順番に出力する際には
以下のようにすることが多いと思います。

for i in div_array:
    print(i)

#[5 6]
#[ 9 10]

ただしlistの[]が表示されていると不正解になることもあり、これを防ぐために以下のように実装を試みました。

引数の end="" にスペースを指定し、5と6 をスペースで分割してその後の空のprint()で次の行に移ることを試みました。

for i in div_array:
    for j in i:
        print(j ,end=" ")
    print()    

>>>5 6 #6、10の後にスペースが入っている
>>>9 10 #6、10の後にスペースが入っている

ですが、この実装では6と10の後にもスペースが入ってしまい不正解となりました。

これを解決するために以下のようにすれば、簡単に解決することができました。
[ ]を外したいリストの前に*をつけて出力することによって、リスト内リストの各要素を分割して表示することができました。

for i in div_array:
    print(*i)

>>>5 6
>>>9 10

まとめ

今回は2次元配列から任意の配列を参照する方法と、2次元リストの出力のことに関して述べました。
かなりマニアックな内容ですが使ってみてください。

【java】型パラメータの型情報を実行時に知るには

$
0
0

0.先に結論

  • 型パラメータT付きでクラス継承する
    • コンストラクタにT型可変長引数を持たせることでTの実際の型が取得できる

1.課題

配列の要素の型は array.getClass().getComponentType() で後から分かるけど、
ArrayList、LinkedList等のListで同じことをやるのは難しい!


2.対象読者

  • javaで Class クラスを利用したことのある人
  • 型パラメータに興味のある人

3.ダメな例


準備

class Hoge
{
    public static void main(String[] args)
    {
        ArrayList<Number> inteList = new ArrayList<>();
        System.out.println(new ParamGetter(inteList).get());
        //ParamGetterクラスのget()メソッドで
        //「Number」などと表示させたい
    }
}

3.1 T.classはできない

class ParamGetter
{
    Class paramClass;
    <T> ParamGetter(List<T> list)
    {
        this.paramClass = T.class;
        //×これはNG
    }
    Class get(){return paramClass;}
}

3.2 リフレクションしても失敗[1]

//(リフレクションしたことある人向け)
class ParamGetter
{
    Type paramType;
    <T> ParamGetter(List<T> list)
    {
        this.paramType = ((ParameterizedType)new ArrayList<T>(){}.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }
    Type get(){return paramType;}
}

結果:「T」と表示されてしまう
Field クラスの getGenericType() メソッドを使う方法[2]でも同じ。


3.3 toArray() で配列化しても失敗

class ParamGetter
{
    Object array;
    <T>ParamGetter(List<T> list)
    {
        this.array = (T[])list.toArray();
        //☆System.out.println((T[])list.toArray());
    }

    Type get() throws NoSuchFieldException, SecurityException
    {
        return this.array.getClass().getComponentType();
    }
}

結果(main):「class java.lang.Object」と表示されてしまう。


4. 根本的な原因

メソッドに渡す段階で型情報が消えている


3.3はまずList<T>型仮引数にArrayList<Number>型実引数を渡している。
次にT型配列に変換して Object 型のフィールドに格納している。
そして、get() が呼ばれると、「実行時の型」(配列型)を求め、その配列の要素の型を調べる。


その結果が Object と出たのだから、
T = Number ではなく T = Object となってしまったことになる。


事実、☆のコードを実行すると、Object[] 型である旨が表示される。
→やはり T[] = Object[]

System.out.println((T[])list.toArray());//Object[]型と出る。

ここから「型パラメータの情報が、引数として渡す間で消えてしまった」ということがわかる。
実際にはコンパイル時に消される(受け取り可能な最も抽象的な型になる)[3]


5. 解決策

実行時に消えるものは例えリフレクションしても取れない(リフレクションは実行時の技術[4])

  • 呼び出すとき、消えないところに書く
  • クラス自体に型パラメータを持たせ、コンストラクタでそれを利用

5.1 型パラメータを引数に置換え

class Hoge<T> Fuga生存 Fuga as
new Hoge(); × インスタンスメソッドが持つ型パラメータ
new Hoge<Fuga>(); クラスが持つ型パラメータ[5]
new Hoge(Fuga.class) Class型引数

△・・・不可能ではないが、冗長な使用になってしまう


5.2 ArrayList<T>
ArrayList_<T>

やりたいこと:
new ArrayList_<Number>(Object...);
new ArrayList<Number>(Object...);
ほぼ同じ意味を持つようにし、
かつ Number を実行時に取得可能にする


ほぼ同じ意味を持つ→継承で実現可能
本記事のsaka1029氏のコメントを参考に書く

public class ArrayList_<T> extends ArrayList<T> {

    public final Class<?> clazz;

    public ArrayList_(T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
                "dummy引数を指定してはいけません");
        clazz = dummy.getClass().getComponentType();
    }

    public Class getComponentClass() {
        return clazz;
    }
}


6 (おまけ) new Hoge<t.getClass()>的なことを可能にする

ジェネリクス型Hoge<T>のインスタンスはClass<T>型の変数からは作れない。


要は new Hoge<t.getClass()> はできない。
メソッド内に書いておくなら new Hoge<?> としてある程度可能だけど、
Hoge<t.getClass()>.getClass().getDeclaredConstructor().newInstance()
として任意の場所でジェネリクス型インスタンスを作ることはできない。


これをしたい場合は、引数にClass<?>型変数を渡せばよい。

public class ArrayList_<T> extends ArrayList<T> {

    public final Class<?> clazz;

    public ArrayList_(T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
                "dummy引数を指定してはいけません");
        clazz = dummy.getClass().getComponentType();
    }

    public ArrayList_(Class<T> clazz)//これを追加
    {
        this.clazz = clazz;
    }

    public Class getComponentClass() {
        return clazz;
    }
}

参考

[1]http://unageanu.hatenablog.com/entry/20071105/1194264963
[2]https://codeday.me/jp/qa/20181216/16785.html
[3]https://qiita.com/pebblip/items/1206f866980f2ff91e77
本文中の「受け取り可能な最も抽象的な型」は[3]の「上限境界」を意味する。
[4]https://java.keicode.com/lang/reflection.php
[5]本記事にお寄せいただいた、saka1029氏のご指摘
その他:https://java-beginner.com/thread-thread-and-runnable/


cakephp配列操作

$
0
0

配列操作

Cake\Utility\Hashを使うと配列の中から特定の値を抽出や挿入・削除
差分取得など色々便利です。

// Hash::extractを使い配列から特定のデータを取得する
$users = [
    ['id' => 1, 'name' => 'mark'],
    ['id' => 2, 'name' => 'jane'],
    ['id' => 3, 'name' => 'sally'],
    ['id' => 4, 'name' => 'jose'],
];
$results = Hash::extract($users, '{n}.id');
// $results は以下のとおり:
// [1,2,3,4];
// 配列に配列を挿入する
$a = [
    'pages' => ['name' => 'page']
];
$result = Hash::insert($a, 'files', ['name' => 'files']);
// $result は以下のようになります:
[
    [pages] => [
        [name] => page
    ]
    [files] => [
        [name] => files
    ]
]

cakephp ドキュメント

わずか5文字!document.querySelectorAllの返り値を配列に変える方法

$
0
0

お前のすべてが欲しい...

結論を知りたい人はスプレッド構文を使う

HTML内の特定の要素をすべて取得したいというときは document.querySelectorAll を使うと便利です。

.item というクラスをすべて取得したいとき

index.html
 <ul>
  <li class="item">item1</li>
  <li class="item">item2</li>
  <li class="item">item3</li>
 </ul>
script.js
const items = document.querySelectorAll('.item');

と、このように簡単にすべての要素が取得できるのです。めでたしめでたし。

・・・が、ひとつ注意点!!

返り値は NodeList

さてこの配列?を操作しようと思ったらここに落とし穴があるのです。そう、返り値は Array ではなく NodeList 。配列ではありません。

NodeList を操作するには

forEach() メソッドを使うのが便利です。

const items = document.querySelectorAll('.item');
items.forEach((item) => {
  console.log(item);
});

だけど配列(Array) にしたい時があるかと思います。

NodeListを配列に変換するには

NodeList | MDN

によると

NodeList は Array とは異なりますが、 forEach() メソッドで処理を反復適用することは可能です。 Array.from() を使うことで Array に変換することができます。

なるほど。 Array.from()メソッド を使えば配列に変換することができるようです。

const items = document.querySelectorAll('.item');
const itemsArray = Array.from(items);

一気に操作したい場合は、

Array.from(document.querySelectorAll('.item'), item => {
 console.log(item);
});

もっと簡単に配列に変換したい

Array.from()メソッドで配列に変換できることはわかりました。だがしかし、駄菓子菓子。もうちょっと簡単に配列に変換する方法があります。

スプレッド構文を使う

const items = [...document.querySelectorAll('.item')];

なんということでしょう。[].だけで配列に変換できました。

スプレッド構文とは

スプレッド構文 | MDN

スプレッド構文を使うと、関数呼び出しでは 0 個以上の引数として、Array リテラルでは 0 個以上の要素として、Object リテラルでは 0 個以上の key-value のペアとして、Array や String などの iterable オブジェクトをその場で展開します。

配列に転換したい場合は、配列の中に iterable なオブジェクトを入れてあげればよいのです簡単ですね。

スプレッド構文の注意点

また、こんなこともMDNのページには書いてありました。

大量の値を展開する場合セクション

JavaScript エンジンには、引数の個数に上限があります。関数呼び出しでのスプレッド構文では、引数の個数がその上限を超えてしまう可能性に留意してください。詳細は apply()のページを参照してください。

引数に上限があるようなので注意しましょう。

スプレッド構文のブラウザー実装状況

スプレッド構文 ブラウザー実装状況

IEでは動きません・・・
適宜トランスパイルをしましょう。

おわりに

記事の最後に「いかがでしたか」と書くとSEO効果あるらしいですよ! ←要出典


参考

Document.querySelectorAll() | MDN
Array.from() | MDN
NodeList | MDN
スプレッド構文 | MDN
Converting a NodeList to an array with vanilla JavaScript
querySelectorAllで帰ってきたNodeListを外部ライブラリを使わずにforEachしたい

PHPの文字列と配列は実行可能?!?!

$
0
0

文字列は実行可能!!!

function example_func() {
  echo 'example_func was called';
}

'example_func'();
// example_func was called

配列も実行可能!!!

class ExampleClass
{
    public static function example()
    {
        echo 'ExampleClass::example was called';
    }
}

['ExampleClass', 'example']();
// ExampleClass::example was called

なにこれ!!!

どちらもコールバック / Callableでタイプヒンティングされた引数に渡せる形式。

callableis_callable関数でtrueと評価されるような、呼び出し可能な引数を定義する際に使います

function call_callback(callable $callback) {
    $callback();
}

// callableでタイプヒンティングされた引数に渡せる形式は3つあります

function example_func() {
  echo 'example_func was called';
}

class ExampleClass
{
    public static function example1()
    {
        echo 'ExampleClass::example1 was called';
    }

    public function example2()
    {
        echo 'ExampleClass::example2 was called';
    }

    public function __invoke()
    {
        echo 'ExampleClass was called';
    }
}

// 1.文字列
// 関数名
call_callback('example_func');
// example_func was called

// 静的メソッド
call_callback('ExampleClass::example1');
// ExampleClass::example1 was called


// 2.配列
// メソッド
call_callback([new ExampleClass, 'example2']);
// ExampleClass::example2 was called

// 静的メソッド
call_callback(['ExampleClass', 'example2']);
// ExampleClass::example1 was called


// 3.__invokeマジックメソッドを実装したオブジェクト
// オブジェクト
call_callback(new ExampleClass);
// ExampleClass was called

// 無名関数
call_callback(function () {
    echo 'closure was called';
});
// closure was called

うーん

callable引数に渡すとき以外使い場所はない
ましてや文字列や配列を直接呼び出すことはないですね

JavaScriptの配列初期化

$
0
0

はじめに

pythonやKotlinでは連続する数の配列などを以下のように簡単に生成することができます。

Numbers.kt
//ラムダ式を用いた初期化
val x = Array(5, { it })
Numbers.py
#rangeを用いた初期化
x = list(range(0,5))

同様に偶数からなる配列は以下のように生成できます。

EvenNumbers.kt
val x = Array(5, { it * 2 })
Numbers.py
#リスト内包表記
x = [x * 2 for x in range(0, 5)]

特にPythonのリスト内包表記を使うと短く書くことができます。今回はこのような配列の初期化をJavaScriptで試してみます。

0で埋めた配列

JavaScript
Array(5).fill(0) //[0, 0, 0, 0, 0]
new Array(5).fill(0) //これも可

JavaScriptの配列生成では[]を用いたリテラル表記が頻出ですが、コンストラクタを用いて生成することもできます。第一引数に配列の長さを指定します。
またJavaScriptの標準ビルトインオブジェクトの一部は、new演算子の省略が認められています。(ただし、new演算子を省略した場合に挙動が変わるものがあるので注意が必要です。)

undefinedで埋めた配列

JavaScript
Array(5).fill() //[undefined, undefined, undefined, undefined, undefined]

0埋めの亜種ですが、fillメソッドの引数を省略することでundefinedで埋めることができます。この形がこれ以降に紹介する派生パターンの基本となります。

ちなみにfillメソッドは必要なの?と感じますが、必要です。

JavaScript
 Array(5) // [ <5 empty items> ]

このように、undefinedで埋められているわけではないことがわかります。これについては、MDNから引用します。

これは arrayLength 分の空のスロットがある配列を意味します。スロットに実際の undefined 値があるわけではありません

連番の配列

JavaScript
Array(5).fill().map((_, i) => i) //[0, 1, 2, 3, 4]

undefined埋めの配列にmapメソッドを適用します。mapメソッドに与える関数の第二引数には配列のインデックスが渡されますので、これを返してやることで連番を生成します。

連続する偶数の配列

JavaScript
Array(5).fill().map((_, i) => i*2) //[0, 2, 4, 6, 8]

Array(5).fill().filter((_, i) => i*2).filter(v => v % 2 === 0) //[0, 2, 4]
Array(5).fill().map((_, i) => i).filter(v => v % 2 === 0) //上に同じ

mapメソッドやfilterメソッドを使うことで実現できます。mapのみで行う方法では配列の長さ(配列の長さが5)を指定、filterを用いる方法は範囲(配列の要素のとる値が0から5まで)を指定できます。

二次元配列

JavaScript
Array(5).fill().map(() => Array(5).fill(0))
//[ [ 0, 0, 0, 0, 0 ],
//  [ 0, 0, 0, 0, 0 ],
//  [ 0, 0, 0, 0, 0 ],
//  [ 0, 0, 0, 0, 0 ],
//  [ 0, 0, 0, 0, 0 ] ]

0埋め配列の変形です。

以下はダメな例です。

JavaScript
Array(5).fill(Array(5).fill(0))
//[ [ 0, 0, 0, 0, 0 ],
//  [ 0, 0, 0, 0, 0 ],
//  [ 0, 0, 0, 0, 0 ],
//  [ 0, 0, 0, 0, 0 ],
//  [ 0, 0, 0, 0, 0 ] ]

このようにしてしまうと、内側の配列は全て同じ場所を参照する配列となってしまいます。つまり以下のように一箇所の値を変更すると他の部分まで変更されてしまいます。

JavaScript
const x = Array(5).fill(Array(5).fill(0))
x[0][1] = 1
console.log(x)
//[ [ 0, 1, 0, 0, 0 ],
//  [ 0, 1, 0, 0, 0 ],
//  [ 0, 1, 0, 0, 0 ],
//  [ 0, 1, 0, 0, 0 ],
//  [ 0, 1, 0, 0, 0 ] ]

他の作り方

Array.from()を用いる方法

JavaScript
Array.from(Array(5)) //[undefined, undefined, undefined, undefined, undefined]
Array.from(Array(5)).fill(0)  //[0, 0, 0, 0, 0]
Array.from(Array(5), (_, i) => i) //[0, 1, 2, 3, 4]

Array()の代わりにオブジェクトを利用する方法

JavaScript
Array.from({length:5}) //[undefined, undefined, undefined, undefined, undefined]
Array.from({length:5}).fill(0)  //[0, 0, 0, 0, 0]
Array.from({length:5}, (_, i) => i) //[0, 1, 2, 3, 4]

lengthプロパティを持つオブジェクトは、配列風のオブジェクトとみなされるためArray.from()で配列に変換可能です。

連番の他の作り方

Object.keys()と...を用いる方法

JavaScript
Array.from(Array(5).keys()) //[0, 1, 2, 3, 4]
[...Array(5).keys()] //[0, 1, 2, 3, 4]

配列のキーを取得するObject.keys()を用います。Array(5).keys()はIteratorを返すのみですのでfor...in文などには用いることができますが、配列としては使えません。Iteratorを配列に変換すのために、Array.fromまたはスプレッド構文を用いています

実用性は無い

文字列から生成

JavaScript
Array.from("0".repeat(5)).map(i => parseInt(i))

ちなみに以下のようにすると二番目にNaNが出てきます。原因がわからないのですが、わかる方がいらっしゃればご教授いただきたいです

JavaScript
Array.from("0".repeat(5)).map(parseInt) //[ 0, NaN, 0, 0, 0 ] なぜ NaN ?

ジェネレータで生成

JavaScript
Array.from((function*(){for(let i = 0; i < 5; i++) yield i})())

コードゴルフみたい。

まとめ

個人的にはArray(5).fill().map(...)が好きですが、Array.from(Array(5), ...)もありかなという気がします。初投稿で拙い点が多々あったと思いますが、最後まで読んでいただきありがとうございました。٩( 'ω' )و

Java単体テストライブラリ-Artery/JUnit4-配列の等値判定

$
0
0

目次 ⇒ Java単体テストライブラリ-Artery-サンプル

package jp.avaj.lib.test;

import static org.junit.Assert.*;

import java.math.BigDecimal;
import java.util.List;

import org.junit.Test;

import jp.avaj.lib.algo.ArList;

/**
 Java単体テストライブラリ-Artery/JUnit4-配列の等値判定

 Arteryではint[] vs Integer[]やint[] vs long[]などの異なるタイプでも判定することができる

 JUnitでは等値判定はassertArrayEqualsを使用する(assertEqualsではない)
 JUnitではint[] vs Integer[]やint[] vs long[]など異なるタイプでは判定できない
 */
public class Q02_01 {
  //////// 以下はArteryのサンプル
  public static void main(String[] args) {
    // テストケースの開始を宣言する ⇒ 集計が不要な場合は不要
    ArTest.startTestCase("Q02_01");

    // int[] vs int[]
    {
      int[] value0 = new int[]{0,1,2};
      int[] value1 = new int[]{0,1,2};
      ArTest.equals("int[] vs int[]","value0",value0,"value1",value1);
      value1 = new int[]{10,11,12};
      ArTest.equals("int[] vs int[] (NG)","value0",value0,"value1",value1);
    }
    // int[] vs Integer[]
    {
      int[] value0 = new int[]{0,1,2};
      Integer[] value1 = new Integer[]{0,1,2};
      ArTest.equals("int[] vs Integer[]","value0",value0,"value1",value1);
      value1 = new Integer[]{10,11,12};
      ArTest.equals("int[] vs Integer[] (NG)","value0",value0,"value1",value1);
    }
    // int[] vs Long[]
    {
      int[] value0 = new int[]{0,1,2};
      Long[] value1 = new Long[]{0L,1L,2L};
      ArTest.equals("int[] vs Long[]","value0",value0,"value1",value1);
      value1 = new Long[]{10L,11L,12L};
      ArTest.equals("int[] vs Long[] (NG)","value0",value0,"value1",value1);
    }
    // int[] vs Double[]
    {
      int[] value0 = new int[]{0,1,2};
      Double[] value1 = new Double[]{0D,1D,2D};
      ArTest.equals("int[] vs Double[]","value0",value0,"value1",value1);
      value1 = new Double[]{10D,11D,12D};
      ArTest.equals("int[] vs Double[] (NG)","value0",value0,"value1",value1);
    }
    // int[] vs BigDecimal[]
    {
      int[] value0 = new int[]{0,1,2};
      BigDecimal[] value1 = new BigDecimal[]{new BigDecimal(0),new BigDecimal(1),new BigDecimal(2)};
      ArTest.equals("int[] vs BigDecimal[]","value0",value0,"value1",value1);
      value1 = new BigDecimal[]{new BigDecimal(10),new BigDecimal(11),new BigDecimal(12)};
      ArTest.equals("int[] vs BigDecimal[] (NG)","value0",value0,"value1",value1);
    }
    // int[] vs List<Integer>
    {
      int[] value0 = new int[]{0,1,2};
      List<Integer> value1 = ArList.construct(new Integer[]{0,1,2});
      ArTest.equals("int[] vs List<Integer>","value0",value0,"value1",value1);
      value1 = ArList.construct(new Integer[]{10,11,12});
      ArTest.equals("int[] vs List<Integer> (NG)","value0",value0,"value1",value1);
    }
    // int[] vs String[] ⇒ 判定できるけど、使わない方がよいかも?
    {
      int[] value0 = new int[]{0,1,2};
      String[] value1 = new String[]{"0","1","2"};
      ArTest.equals("int[] vs String[]","value0",value0,"value1",value1);
      value1 = new String[]{"10","11","12"};
      ArTest.equals("int[] vs String[] (NG)","value0",value0,"value1",value1);
    }
    // String[] vs String[]
    {
      String[] value0 = new String[]{"0","1","2"};
      String[] value1 = new String[]{"0","1","2"};
      ArTest.equals("String[] vs String[]","value0",value0,"value1",value1);
      value1 = new String[]{"10","11","12"};
      ArTest.equals("String[] vs String[] (NG)","value0",value0,"value1",value1);
    }
    // String[] vs List<String>[]
    {
      String[] value0 = new String[]{"0","1","2"};
      List<String> value1 = ArList.construct(new String[]{"0","1","2"});
      ArTest.equals("String[] vs List<String>[]","value0",value0,"value1",value1);
      value1 = ArList.construct(new String[]{"10","11","12"});
      ArTest.equals("String[] vs List<String>[] (NG)","value0",value0,"value1",value1);
    }

    // テストケースを終了する ⇒ 集計が不要な場合は不要
    ArTest.endTestCase();
  }

  //////// 以下はJUnit4のサンプル
  // int[] vs int[] - 等しい場合 ⇒ しかしNGになる
  // 「assertArrayEqualsを使う」ということを知らない場合は間違うかも..
  @Test
  public void test00() {
    assertEquals(new int[]{0,1,2},new int[]{0,1,2});
  }

  // int[] vs int[] - 等しい場合 ⇒ これでOK
  @Test
  public void test01() {
    assertArrayEquals(new int[]{0,1,2},new int[]{0,1,2});
  }

  // int[] vs Integer[] - 等しい場合 ⇒ これはコンパイルエラー
  // このくらいはやってほしいけど...
  @Test
  public void test02() {
//    assertArrayEquals(new int[]{0,1,2},new Integer[]{0,1,2});
  }

  // int[] vs long[] - 等しい場合 ⇒ これもコンパイルエラー
  // このくらいはやってほしいけど...
  @Test
  public void test03() {
//    assertArrayEquals(new int[]{0,1,2},new long[]{0,1,2});
  }

  // double[] vs double[],判定誤差を指定する必要がある ⇒ 等しい場合
  @Test
  public void test04() {
    assertArrayEquals(new double[]{0D,1D,2D},new double[]{0D,1D,2D},0.5D);
  }

  // double[] vs double[],判定誤差を指定する必要がある ⇒ 等しくない場合
  @Test
  public void test05() {
    assertArrayEquals(new double[]{0D,1D,2D},new double[]{10D,11D,12D},0.5D);
  }


  // String[] va String[] - 等しい場合
  @Test
  public void test06() {
    assertArrayEquals(new String[]{"a","b","c"},new String[]{"a","b","c"});
  }
  // String[] va String[] - 等しくない場合
  @Test
  public void test07() {
    assertArrayEquals(new String[]{"a","b","c"},new String[]{"x","y","z"});
  }
}

結果は次の通り
無題.png

result.txt
**** Q02_01 start ****
OK int[] vs int[]:value0=[0, 1, 2]:value1=[0, 1, 2]
NG int[] vs int[] (NG):value0=[0, 1, 2]:value1=[10, 11, 12]
jp.avaj.lib.test.Q02_01.main(Q02_01.java:32)
OK int[] vs Integer[]:value0=[0, 1, 2]:value1=[0, 1, 2]
NG int[] vs Integer[] (NG):value0=[0, 1, 2]:value1=[10, 11, 12]
jp.avaj.lib.test.Q02_01.main(Q02_01.java:40)
OK int[] vs Long[]:value0=[0, 1, 2]:value1=[0, 1, 2]
NG int[] vs Long[] (NG):value0=[0, 1, 2]:value1=[10, 11, 12]
jp.avaj.lib.test.Q02_01.main(Q02_01.java:48)
OK int[] vs Double[]:value0=[0, 1, 2]:value1=[0, 1, 2]
NG int[] vs Double[] (NG):value0=[0, 1, 2]:value1=[10, 11, 12]
jp.avaj.lib.test.Q02_01.main(Q02_01.java:56)
OK int[] vs BigDecimal[]:value0=[0, 1, 2]:value1=[0, 1, 2]
NG int[] vs BigDecimal[] (NG):value0=[0, 1, 2]:value1=[10, 11, 12]
jp.avaj.lib.test.Q02_01.main(Q02_01.java:64)
OK int[] vs List<Integer>:value0=[0, 1, 2]:value1=[0, 1, 2]
NG int[] vs List<Integer> (NG):value0=[0, 1, 2]:value1=[10, 11, 12]
jp.avaj.lib.test.Q02_01.main(Q02_01.java:72)
OK int[] vs String[]:value0=[0, 1, 2]:value1=[0, 1, 2]
NG int[] vs String[] (NG):value0=[0, 1, 2]:value1=[10, 11, 12]
jp.avaj.lib.test.Q02_01.main(Q02_01.java:80)
OK String[] vs String[]:value0=[0, 1, 2]:value1=[0, 1, 2]
NG String[] vs String[] (NG):value0=[0, 1, 2]:value1=[10, 11, 12]
jp.avaj.lib.test.Q02_01.main(Q02_01.java:88)
OK String[] vs List<String>[]:value0=[0, 1, 2]:value1=[0, 1, 2]
NG String[] vs List<String>[] (NG):value0=[0, 1, 2]:value1=[10, 11, 12]
jp.avaj.lib.test.Q02_01.main(Q02_01.java:96)
**** Q02_01 summary ****
test count = 18
success    = 9

c++ 初学者がvector.at()で配列要素を指定する理由

$
0
0

c++の配列を要素指定する際に配列要素.at()または配列要素[i]で要素指定する方法があると思います。

よくc++やプログラミング初学者はvector.at()を使うことを推奨されるのですが、なぜなのか理由をみていきます。

環境

実行環境はGCC5.4.1です。(AtCoderのコードテストと同じ)

理由:配列要素[i]で要素指定するとエラー文が出力されない

以下のようなコードがあるとします。

#include <bits/stdc++.h>
using namespace std;

int main(){
    vector<int> vec(5);
    for(int i = 0;i<5;i++){
      cin >> vec.at(i);
      cout << vec.at(i) << endl;
    }

}

このコードは

1 2 3 4 5

のような入力を取ってきて出力するだけの簡単なコードです。

この配列要素を指定している箇所のvec.at()を

cin >> vec[i];
cout << vec[i] << endl;

と書く2通りの書き方があるのですが、vec[i]で要素指定した場合は例えば
セミコロンを書くのを忘れてエラーになった場合にエラー文が出力されません。

なので、c++の文法に慣れてないうちはvec.at()で書くことが一般的には推奨されています。

ですが、個人的にはやはりvec[i]で要素指定する方がタイプしやすく他の言語の人がみたときの可読性も上がるのでエラーが出たときに要素指定の箇所を適宜コメントアウトしてvec.at()に置き換えてエラー文を出力させるといった方法を取るのがいいなと思います。

以上です。

文字列を1文字ずつ格納する

$
0
0

文字列を1文字ずつ配列に格納してみた。

str = "apple"
ch = str.split("")
print ch

<コンソール>

["a","p","p","l","e"]

と表示される。

配列の要素1つ1つにアクセスしてみる。

<前提条件>
while文(繰り返し処理)に使う+配列の要素番号の変数cntを用意する。

cnt = 0
while cnt < 5 do
    p ch[cnt]
    cnt = cnt + 1
end

<補足>
chは配列名、ch[0]は配列1番目、ch[1]は配列2番目の意味。
※配列は0番目からはじまる。要素数が5個あれば、
 配列の箱が0~4個目までの計5個作られるイメージ。

while文の特徴
whileは条件式が真(true)の間のみ、
while文内の処理を繰り返す。

cnt = 0
while cnt < 5 do
    p ch[cnt]
    cnt = cnt + 1
end

<解説>
まずは変数cntに数値0をセット。
続いてcntの値が0な状態で、条件式[0 < 5]を実行。
数値0は数値5よりも小さいので、この条件式は
正しい、true(真)となるため、
while文のdo配下の処理が実行される。
p ch[cnt]
※pメソッド(画面出力)
※ch[cnt]
 cntが0の際は、cnt[0]となり、
 cntが1の際は、cnt[1]となる。
※変数cntは「cnt = cnt + 1」と記載し、
 while文内の処理が一回実行されるごとに、
 変数cntに加算1をしている。

<コンソール>

"a"
"p"
"p"
"l"
"e"

【JavaScript】map関数を用いたおしゃれな配列処理

$
0
0

はじめに

決まった繰り返し処理といえばforですが、JavaScriptのES6以降ではmap関数を使ってもっと簡単でおしゃれな書き方で書けます。

map関数は各要素に対して決まった繰り返し処理をして、新しい配列を生成するのに使える便利な関数です

例として、[1,2,3,4,5,6]といったような各要素を二乗した配列を作ることを考えます。

通常のfor文から書き換え

まずは通常のfor文で書いてみます。

const list = [1,2,3,4,5,6];
const newList = [];

for(let i = 0; i< list.length ; i++){
    newList[i] = list[i]*list[i];
}

console.log(newList); //[1,4,9,16,25,36]

list[i]が煩わしい気がします。
これをforEach文で書き直すとこうなります。

const list = [1,2,3,4,5,6];
const newList = [];

list.forEach(function(item, index){
  newList[index] = item*item;
}

console.log(newList);

newListの宣言が煩わしい気がします。
これをmapで修正するとこうなります。

const list = [1,2,3,4,5,6];

const newList = list.map(function(item){
    return item*item;
});

console.log(newList) //[1,4,9,16,25,36]

returnされたものが新しい配列の要素一つになります。

新しい配列はlistから生成するんだ」ということが一瞬で伝わって、非常にすっきり書けていますね。
自分自身を更新(して複製)」する時にMapは便利です。

これを使うとReactのDOMを複数生成するのに便利だったりします。

参考

この記事は「CodeShip」内での実際の質疑応答や指導・アドバイスの一部を基に作成しています。

Excelのセルを配列に取り込むVBA

$
0
0

VBAで配列を使う理由

せっかく、Excel VBAで処理を自動化しても、1セルずつ入出力していると、処理にかかる時間が長くなってしまう。特に、Excel VBAの場合、セルへの出力に時間がかかるので、大量のデータを処理する場合は、1セルずつ処理をして出力するよりも、配列で処理をして、一括で出力することで、処理の時間を短くできる。

具体例

A列の数字と、B列の数字を掛け算して、100007で割った余りを出力する

処理をするExcel Data

A列とB列に0~10000の乱数を入力 (1行目~50000行目まで)
image.png

例1: 一行ずつ処理する場合

simple_code
Public Sub test_case_1()
    Const DIV As Long = 100007
    Dim i As Long
    For i = 1 To 50000
        Range("C" & i).Value = Range("A" & i).Value * Range("B" & i).Value Mod DIV
    Next
End Sub

計算時間は、2.9921875秒
1行ずつ入出力しているので、処理が遅い。

例2: Rangeオブジェクトで処理する場合

using_object
Public Sub test_case_2()
    Const DIV As Long = 100007
    Dim i As Long 
    Dim r_in As Range, r_out As Range
    Set r_in = Range("A1:B50000")
    Set r_out = Range("C1:C50000") 
    For i = 1 To 50000
        r_out(i, 1) = r_in(i, 1) * r_in(i, 2) Mod DIV
    Next 
End Sub

計算時間は、2.3984375秒。
Rangeオブジェクトを使って、一括で情報を入力できるため、少し計算が早くなる。
しかし、1行ずつセルに出力させているので、まだ処理が遅い。一括で出力できると、もっと早くなるはず。

例3: 配列を使って処理する場合

using_array
Public Sub test_case_3()
    Const DIV As Long = 100007
    Dim i As Long
    Dim v_in As Variant, v_out As Variant

    v_in = Range("A1:B50000") ' Setをつけずに、Variant型変数にRangeオブジェクトを代入すると配列になる
    v_out = Range("C1:C50000") ' C1:C50000が空白なら空の配列になる

    For i = 1 To 50000
        v_out(i, 1) = v_in(i, 1) * v_in(i, 2) Mod DIV
    Next
    Range("C1:C50000") = v_out '一括でExcelシートに出力
End Sub

計算時間は、0.140625秒。
入力も出力も、一括して行っているので、処理時間が短い。
入力よりも、Excelワークシートへの出力を一括して行う方が、処理時間を大きく短縮できる。

計算時間比較 (10回分)

test_case 1 test_case_2 test_case_3
2.9921875 2.3984375  0.140625  
2.9375 1.9765625 0.109375
2.9921875 1.9765625 0.140625
2.9375 1.9453125 0.140625
2.84375 2.328125 0.1328125
2.9296875 2.046875 0.1484375
3.0078125 1.984375 0.1328125
2.953125 1.921875 0.1953125
3.3359375 1.984375 0.1484375
2.8828125 2.015625 0.15625

配列の使い方

セル範囲を配列に変換

例えば、以下のような3行4列の範囲のRangeオブジェクトを配列に渡すと、3 x 4の配列ができる。
image.png

array_intro
Public Sub get_array()
    Dim r As Range
    Dim v As Variant
    Set r = Range("A1:D3")
    v = r 'Rangeオブジェクトの中身が配列に格納される
End Sub

ローカルウィンドウで、配列の中身を見てみると、以下のようになっている。
v(i, j) は i行 j列目の要素に対応していることが分かる。
Variant型なので、String型、Double型、Boolean型も一つの配列に入れることができる。
image.png

配列で各要素ごとにデータ処理 (Lbound, Ubound関数)

得られた2次元配列v(i, j)の行数、列数は、Lbound, Ubound関数で取得できる。
Lbound, Ubound関数の第2引数は、要素範囲を取得する次元を指定している。

array_check
Public Sub check_array()
    Dim r As Range
    Dim v As Variant
    Set r = Range("A1:D3")
    v = r
    Debug.Print LBound(v, 1), UBound(v, 1) '配列vは、1行~3行まである → 1, 3が出力
    Debug.Print LBound(v, 2), UBound(v, 2) '配列vは、1列~4列まである → 1, 4が出力
End Sub

得られた配列に、以下の処理をして、Range("A5:D7")に出力してみる
1行目は、大文字に変換
2行目は、5を足す
3行目は、論理否定する

array_proc
Public Sub proc_array()
    Dim r_in As Range, r_out As Range
    Dim v As Variant
    Dim i As Long, j As Long

    Set r_in = Range("A1:D3")
    Set r_out = Range("A5:D7")
    v = r_in
    For i = LBound(v, 1) To UBound(v, 1)
        For j = LBound(v, 2) To UBound(v, 2)
            If i = 1 Then v(i, j) = UCase(v(i, j)) '1行目の処理
            If i = 2 Then v(i, j) = v(i, j) + 5    '2行目の処理
            If i = 3 Then v(i, j) = Not v(i, j)    '3行目の処理
        Next
    Next
    r_out = v

End Sub

出力結果は、以下の通り。
image.png

まとめ

Excelのセルの範囲を配列に入出力することは、簡単にできる割にはあまり活用されていない。
処理の高速化にもつながるので、活用できるようになりたい。

PHPの配列操作がめんどいのでRxPHPを導入する

$
0
0

RxPHPを使ってみようと思った経緯

PHPで複雑な配列操作をしていると辛くなった事があったので、良い解決策がないかと思ってRxPHP調べたらいい感じにかけそうだったので使ってみた

PHPの配列操作はめんどくさい

PHPには配列操作用にarray_map、array_filter、array_reduceなど
配列が操作できる関数が色々ある
基本的にコールバック関数を使って操作する

配列操作の例 (3文字よりでかい単語だけ抽出して文字の合計を算出)

例1(一旦変数に渡す)
$languages = ['PHP', 'PYTHON', 'RUBY', 'GOLANG'];

$lengths = array_map(
    function ($lang) { return str_len($lang) },
    $languages
);

$filterd = array_filter(
    $lengths,
    function ($length) { return $length > 3 }
);

$sum = array_reduce(
    $filtered,
    function ($x, $y) { return $x + $y }
);

これだといちいち変数に入れるんめんどい

例2(処理をネストする)
$languages = ['PHP', 'PYTHON', 'RUBY', 'GOLANG'];

$sum = array_reduce(
    array_filter(
        array_map(
            function ($lang) { return str_len($lang) },
            $languages
        ),
        function ($length) { return $length > 3 }
    ),
    function ($x, $y) {
        return $x + $y
    }
);

ネストして見にくいしコードの修正がしにくくてめんどい

Javascriptみたいにメソッドチェーンで書きたいと思ってたらRxPHPで書けそうだった

JSで書いた場合

const languages = ['PHP', 'PYTHON', 'RUBY', 'GOLANG'];
const sum = languages
.map(lang => lang.length)
.filter(length => length > 3)
.reduce((x, y) => x + y);

JSみたいにメソッドチェーンで書けると見やすいし、いちいち変数を作らなくていい

RxPHPを使ってみる

RxPHPはReactive ProgrammingをするためのライブラリReactiveXらしく、オブザーバーパターンとかイテレーターパターンとか使ってるみたい
配列以外にも、イベント処理とか非同期処理にも使えるけど、PHPではライブラリ(ReactPHPとか)入れないと使えないので、普通は配列以外では使わんかなと思うので、とりあえずややこしい話はどうでもいい

RxPHPの構成要素

Observable, Observer, Operatorぐらい分かっとけば配列操作では困らないと思う

Observable

データをぶん投げるやつ

Observer

データをObservableから受け取って処理する

Operator

Observerに渡す前にデータを加工できる
PHP関数のarray_map, array_filter, array_reduceみたいなやつ
他色々処理ができる
何種類あるかは知らん

Stream

ObservableからObserverまでの流れをStreamと言ってるみたい

RxPHPでコードを書いてみる

インストール

composer require reactivex/rxphp

さっきの例と同じコードをかく

RxPHP
use Rx\Observable;
use Rx\Scheduler;
use Rx\Scheduler\ImmediateScheduler;

// スケジューラーの設定
$immediateScheduler = new ImmediateScheduler();
Scheduler::setDefaultFactory(function () use ($immediateScheduler) {
    return $immediateScheduler;
});

// 配列処理
const languages = ['PHP', 'PYTHON', 'RUBY', 'GOLANG'];
Observable::fromArray(languages) // Observable
    ->map(function ($lang) { // Operator
        return str_len($lang);
    })
    ->filter(function ($length) { // Operator
        return $length > 3;
    })
    ->reduce(function ($x, $y) { // Operator
        return $x + $y;
    })
    ->subscribe( //Observer
        function ($sum) {
            print("合計は$sum")
        },
        // どっかでエラーが出たら発火
        function (\Exception $ex) {
            print('失敗したよ')
        },
        // 投げたデータの処理が全て完了したら発火
        function () {
            print('完了したよ')
        });

Schedulerを設定しないと怒られたので設定
Schedulerは、どう実行するのか(スレッド分けたり、処理の順番変えたり)決められるが、PHPはライブラリ入れんとスレッド分けれんし基本逐次処理なので即座に実行してくれるimmediateSchedulerを設定する
なんでimmediateSchedulerがデフォルト設定じゃないんかな?
設定しなくてもいい気がするのでなんかミスってるかも

Rx\ObservableのfromArrayメソッドで配列を投げる
map・filter・reduceメソッドがOperatorにあたる
subscribeがObserverにあたって、データ受け取り時、処理エラー時、完了時にそれぞれ処理が書ける

まとめ

この記事では、RxPHP使った方がコード量は多くなったが、複雑な配列処理をめっちゃ書くプロジェクトがあれば、効力(可読性とか保守性とか)を発揮するはず
単純な処理しかない場合は、既存の関数で十分かなと思う

【JavaScript】Object とは似て非なる Array の異常さを明らかにする ~exotic object とは~

$
0
0

イントロダクション

そもそも array とは0番目の値、1番目の値、...と一列に値を並べたもので、 object は key-value のペアの集まりであり、まったく異なるデータ構造である。

しかし、array は object としての性質も持っており、自由にプロパティの追加などができる。

const arr = [1, 2, 3]
arr.prop = "value"

一方で、length プロパティに代入できる値に制限があるなど、object とは異なる部分もある。

const a = []
a.length = -2  // Uncaught RangeError: Invalid array length

「単に setter/getter 持ちのプロパティ(acessor property1)なのでは?」と思うかもしれないが、実はそうではなく、普通の data property1 である。この記事では、このような array の異常性を列挙した上で、それを実現する仕組みを説明する。

1. 持っているメソッドが違う

array object は .slice() メソッドなど、通常のオブジェクトにはないメソッドを持っているが、これは簡単に説明がつく。

array object は Array.prototype を継承しており、さらにこれは Object.prototype を継承している。Array.prototype がいくつかのメソッドを定義しているため、array object は object が持つメソッドだけでなく array 特有のメソッドにもアクセスできるのである。array が iterable であるのも、Array.prototype[Symbol.iterator] メソッドを持っているからである。

また、以下のように toString() メソッドの挙動が異なることも、「Object.prototype が持つ toString() メソッドを Array.prototype がオーバーライドしているから」と説明がつく。

const obj = {0: 1, 1: 2}
const arr = [1, 2]

obj.toString()  // "[object Object]"  <- Object.prototype.toString()
arr.toString()  // "1,2"              <- Array.prototype.toString()

prototype chain.png

2. length プロパティ

array object の length プロパティは特別なものであり、普通のプロパティとは異なる振る舞いをする。ここで、length が data property1 であることを確認しておこう。

Object.getOwnPropertyDescriptor([], "length")
// {value: 0, writable: true, enumerable: false, configurable: false}

もし data property ではなく accessor property であれば、valuewritable の代わりに getset が存在するはずである。length プロパティには getter も setter もないことが分かる。

3. length 以上のインデックスに代入すると length も増える

length 以上のインデックスに対して代入をすると、length の値はそのインデックス+1になる。

const arr = []
arr.length  // 0

arr[2] = 10
arr.length  // 3
arr         // [empty, empty, 10]

当たり前と言えばそうだが、object として考えると異常な振る舞いである。新しいプロパティを追加しただけで、別の data property の値が変わるというのは、普通の object ではありえない

ちなみに、要素を delete しても length は変わらない。

const arr = [1, 2, 3]
arr.length  // 3
delete arr[2]
arr.length  // 3
arr         // [1, 2, empty]

4. length プロパティを non-writable にすると length 以上のインデックスに代入できない

length プロパティの writable 属性を false にして2値を変更できなくすると、length 以上のインデックスに対する代入ができなくなる。

const arr = Object.defineProperty([], "length", {writable: false})
arr[3] = 10
arr         // []
arr.length  // 0

strict モードならエラーが発生する。

"use strict"
const arr = Object.defineProperty([], "length", {writable: false})
arr[3] = 10  // Uncaught TypeError

3. のことを考えれば納得できるが、やはり普通の object ではありえない挙動である。

5. length プロパティを変えると実際の要素数も変わる

length を減らすと、length 以上のインデックスを持つ要素は削除される。

const arr = [1, 2, 3, 4, 5]
arr.length = 3
arr  // [1, 2, 3]

該当するプロパティも消えていることが分かる。

Object.getOwnPropertyNames(arr)  // [ '0', '1', '2', 'length' ]

このように、length プロパティを変えることで他のプロパティに影響を与えるという点においても、array object が異常であることが伺えるだろう。

逆に length を増やすと、empty な要素が末尾に追加される。

const arr = [1, 2, 3]
arr.length = 6
arr  // [1, 2, 3, empty, empty, empty]

しかし、プロパティの数が増えるわけではない。length は 6 であるのに、3 4 5のプロパティは存在しないことが分かる。

Object.getOwnPropertyNames(arr)  // [ '0', '1', '2', 'length' ]

empty な要素というのは、該当するプロパティが存在しない要素を意味するのである。

6. length を減らすときに non-configurable な要素があるとそこで止まる

length を減らそうとすると、削除される予定の要素の中にもし configurable 属性が false であるものがいくつかあれば、その中で最も大きいインデックスの所で length が止まる。これは、non-configurable なプロパティは削除できないからであるが、それでも中途半端に長さが減るというのは意外である。

const arr = ["a", "b", "c", "d", "e"]

// プロパティ "2" を non-configurable にする
Object.defineProperty(arr, "2", {
  configurable: false
})

arr.length = 0
arr.length  // 3
arr         // ["a", "b", "c"]

array length change.png

これも、strict モードではエラーが発生するが、length が減らなくなるわけではない。

"use strict"
const arr = ["a", "b", "c", "d", "e"]

Object.defineProperty(arr, "2", {
  configurable: false
})

arr.length = 0  // Uncaught TypeError: Cannot delete property '2' of [object Array]
arr.length      // 3

7. length は 0 から 2^32 - 1 まで

array object の 長さは 0 から 2^32 - 1 までの整数値しかとれない。これも array の長さを表すという点では不思議なことではないが、setter があるわけでもないのにある種の validation が行われるのはやはり異常である。

const arr = []
arr.length = -1       // Uncaught RangeError: Invalid array length
arr.length = 1.5      // Uncaught RangeError: Invalid array length
arr.length = 2 ** 32  // Uncaught RangeError: Invalid array length

ちなみに、上記の条件さえみたせば数値の string 表現でもよい。

const arr = []
arr.length = "3"
arr.length  // 3

もっと言えば、Number() 関数に渡して有効な数値になる値ならばなんでもよい。

const arr = []
const length = { valueOf: () => 3 }
Number(length)  // 3
arr.length = length
arr.length  // 3

ちなみに内部的にはこの数値変換はなぜか2回行われる。

const arr = []
const length = { valueOf: () => { console.log("oops"); return 3 } }
arr.length = length  // "oops" が2回出力される

8. インデックスは 0 から 2^32 - 2 まで

length と同様に、 array インデックスとして有効な整数は 0 から 2^32 - 2 までであり、それ以外の値をキーにもつプロパティを作っても、配列の要素とはならない。

const arr = []

arr[2 ** 32 - 1] = 1  // 範囲外
arr.length  // 0
Object.getOwnPropertyNames(arr)  // ["length", "4294967295"] (プロパティとしては追加されている)

arr[2 ** 32 - 2] = 1  // 範囲内
arr.length  // 4294967295

タネ明かし

ここまでで述べたような array object の特殊な挙動はいったいどのようにして実現されているのだろうか?

実は JavaScript の object は「プログラマーからは一切見えない内部的なメソッド」を持っている。これらのメソッドはその object に対して何らかの操作をしたときにそれに対応したものが呼び出されるようになっている。Array object はこのうち [[DefineOwnProperty]] というメソッドだけ、通常の object とは異なった定義がされているのである。下記のリンクは ECMAScript 2019 の仕様の該当部分である。

内部メソッドは object が作られるときに、その object の内部スロットに明示的にセットされる。例えば array が作られるときは下のように、通常 object とは異なる [[DefineOwnProperty]] メソッドがセットされるのである。

...
5. Let A be a newly created Array exotic object.
6. Set A's essential internal methods except for [[DefineOwnProperty]] to the default ordinary object definitions specified in 9.1.
7. Set A.[[DefineOwnProperty]] as specified in 9.4.2.1.
...
(from 9.4.2.2 ArrayCreate)

Side Note: 普通のプロパティやメソッドと異なり、内部メソッドは継承されない。例えば array の slice メソッドは Array.prototype から継承したものであり、array が作られるたびにわざわざメソッドをセットしているわけではない。しかし、内部メソッドは object を作るたびに毎回その内部スロットにセットするようになっている

このように、通常の object の内部メソッドをオーバーライドしているような object を exotic object といい(exotic=風変わりな)、例としては Array、TypedArray、argumentsbind した function、proxy object などがある。

この [[DefineOwnProperty]] メソッドは下の図のように、プロパティへの代入が行われるときに呼び出される(あくまで仕様を追うときに参考になればいいという図)。

js property assign under the hood

下のように、array object は [[DefineOwnProperty]] メソッドにおいてプロパティ名が length のときとインデックスであるときに特別な処理をしていることが分かる。ここまでで説明した array object の特殊な挙動は、この内部メソッドを見ればすべて説明がつく。

array_defineownproperty.png

まとめ

array object は基本的には object であるが、普段使う JavaScript の知識だけでは説明できないような振る舞いをする。このような奇妙な振る舞いを理解するためには、言語仕様で定義されている内部メソッドなどの低レベルな所まで見なければいけない。筆者は array の「object っぽいけど違う」所が気持ち悪く感じ、ネット上で調べても答えが得られなかったため、自分で言語仕様を読んで理解し記事を書くに至ったが、もし他の誰かの参考になれば幸いである。

仕様の該当箇所

本文中にあげた部分は除く。


  1. プロパティには2種類あり、data property と accessor property である。前者は普通のプロパティで、一つの値を格納するだけである。後者は getter と setter と呼ばれる関数によってそれぞれ"値の取得"、"値の代入"を実現するものである。 

  2. array の length プロパティは最初から non-configurable なので writable 属性を変更できないはずでは?と思うかもしれないが、実は一般的に non-configurable であっても writabletrue から false にすることは特別に許されている。仕様の該当部分: 9.1.6.3 ValidateAndApplyPropertyDescriptor 

配列のグループ分け

$
0
0

配列のグループ分け

Laravelを使って班分け(グループ分け)してみたというのを見たのですが、devide関数ややこしくない?

PHPには配列を分割するarray_chunkという関数が最初から用意されているのですが、参照先の参照先にもあるように末尾処理が微妙です。
10個の配列を4分割すると[3個, 3個, 3個, 1個]になってしまうのです。

そんなわけで元記事ではarray_sliceで切り出しているのですが、正直なところわかりにくいので、もっと簡単にやりましょう。

    /**
     * 配列を分割して返す
     * @param array 元配列
     * @param int 分割数
     * @return array 元配列を分割したやつ
     */
    function divide(array $arr, int $division):array{
        $ret = [];
        $cnt = 0;
        foreach ($arr as $k => $v) {
            $ret[$cnt++ % $division][$k] = $v;
        }
        return $ret;
    }

    $a = range(0, 9);
    shuffle($a);
    $ret = divide($a, 4); // [3個, 3個, 2個, 2個]

できた。

元記事ではしていなかったのですが、一応キーも保持するようにしています。

おわりに

作ってから気付いたんだけど動作違うわこれ。

array_chunkや元記事は、分割後の配列も並んでいる順に整列します。
ABCDEFGHIJが並んでいたら[ABC, DEF, GH, IJ]となります。

対してこちらは、レジに並ぶ客のように分かれます。
ABCDEFGHIJ[AEI, BFJ, CG, DH]になるということです。

そんなわけで元記事をそのまま差し替えるには至りませんでした。
残念。

ここまで全部間違い

以下2019/10/24追記

ここまでリリースしたあとで突っ込まれたわけですが、元記事を読み返してみたら設問が根本的に違う。

・1組あたりの人数は4人で、グループ数は可変。
・端数が出たら5人組にする。
・人数が少ない場合は特殊処理。

いったい何をキメていたら、この設問から上のdivide()みたいな関数を作れるんですかね??

ということで作りなおしましょう。
人数が少ないときの挙動がよくわからないので列挙しておきます。

・5人以下:[n]
・6人:[3, 3]
・7人:[4, 3]
・8人:[4, 4]
・9人:[5, 4]
・10人:[5, 5]
・11人:[4, 4, 3]
・12人:[4, 4, 4]
・13人以上:定義通り

グループあたり人数を可変にするとこのあたりの動作がよくわからなくなるので、以下では4人固定としておきます。

/**
 * 配列を分割して返す
 * @param array 元配列
 * @return array 元配列を分割したやつ
 */
function divide(array $arr): array {
    $cnt = count($arr);

    // 6未満
    if ($cnt < 6) {
        return [$arr];
    }

    // 11未満
    if ($cnt < 11) {
        return array_chunk($arr, ceil($cnt / 2));
    }

    // 4の倍数と11
    if (!($cnt % 4) || $cnt === 11) {
        return array_chunk($arr, 4);
    }

    // それ以外
    $ret = array_chunk($arr, 4);
    $extra = array_pop($ret);
    $cnt = 0;
    foreach ($extra as $v) {
        $ret[$cnt++][] = $v;
    }
    return $ret;
}

// 確認
for ($a = 1; $a < 20; $a++) {
    var_dump( divide(range(1, $a)) );
}

うーん、元記事とたいして変わってない。
これならわざわざ書き換える意味がないかんじですね。

先にグループ数を求めてから配分したら多少行数が減るかな?

function divide(array $arr): array {
    // 分割数
    $cnt = count($arr);
    if ($cnt < 6) {
        $division = 1;
    } elseif ($cnt < 11) {
        $division = 2;
    } elseif ($cnt < 12) {
        $division = 3;
    } else {
        $division = floor($cnt / 4);
    }

    // 分割する
    $cnt = 0;
    foreach ($arr as $k => $v) {
        $ret[$cnt++ % $division][$k] = $v;
    }
    return $ret;
}

行数は減りましたが、読みやすくなったかというとどうでしょうね。
というか11が厄介だな。

やろうと思えば$divisionの算出は1行でできますが、さらにわかりにくくなるだけなので辞めましょう。

やろうと思ったら読めなくなった
function divide(array $arr): array
{
    $cnt = 0;
    foreach ($arr as $k => $v) {
        $ret[$cnt++ % (($c = count($arr)) < 12 ? $c < 11 ? 1 + floor($c / 6) : 3 : floor($c / 4))][$k] = $v;
    }
    return $ret;
}

さて、今度こそ設問に間違いはないよな?よな?

Viewing all 821 articles
Browse latest View live