dip Engineer Blog

Engineer Blog
ディップ株式会社のエンジニアによる技術ブログです。
弊社はバイトル・はたらこねっとなど様々なサービスを運営しています。

ディップは新卒一年生でも活躍できる爆速成長環境だった

はじめに

エンジニア1年の振り返りとして 新卒研修とそれぞれの配属されてからのお話しをしたいと思います。

新卒研修

研修内容について

開発研修ではディップでの開発業務の基礎を学び、実践しました。 新卒3人で2ヶ月間にわたり、1つのシステムをリリースしました。

先輩方から実際の業務レベルの指導を受けながら 19新卒主導で要求整理からリリースまで行いました!

具体的には、

  1. リクエスタからの要望を整理(要求整理)
  2. 必要な要件の定義(機能要件定義・非機能要件定義)
  3. システム設計・DB設計
  4. 開発
  5. リクエスタとの受け入れなどの調整
  6. リリース
  7. ユーザーマニュアルの作成

を行いました。

研修を通して、私たちは開発業務の基本を学ぶことができました。 特に今でも学べてよかったと思う内容として、

  • 社内のウォーターフォール開発の進め方
  • チーム開発でのgithubの運用
  • 「自分が作りたいものをつくる」個人開発との違い
  • プロジェクト全体のタスク管理の大切さ
  • 先輩/上長への進捗の報連相
  • 基本的な資料作成

...など、配属前に大切さを実感できてよかったと思います。

作り上げたもの

「社内用語がいい感じにわかる」物が欲しいという要求に対して、 私たちはSlackで知らない単語を簡単に検索できるシステム「スラケン」を リリースしました。

f:id:satoshi-baba:20200408144908p:plain

Slackでask "知りたい単語"を送信すると、登録されている言葉を検索し、 送信したチャンネルに検索結果を返してくれます。 検索結果には正式名称や読み方、その他の呼び方と意味が表示されます。

f:id:satoshi-baba:20200408144927p:plain

発音や綴りがわからない言葉も検索できるように、あいまい検索を実装しています。 あいまい検索の解説はこちら

f:id:satoshi-baba:20200408144941p:plain

新卒研修でリリースして数ヶ月経ったスラケンですが、今も部署全体で使われており、他の部署でも使いたい!と要望が出ているサービスです。 自分たちで苦労して作ったサービスが使われて、先輩たちが盛り上がっているのを見ると、サービスの開発って楽しい!もっと使われるようにしたい! と開発がもっと好きになりました。

個別の振り返り

@ogihara_xxx

初めましてこんにちは。19新卒でモバイルアプリ開発を担当している@ogihara_xxxです。
今回はディップに入社して1年目の様子について紹介したいと思います。

1年目、どんな事した?

私は、「バイトル」のモバイルアプリを開発するチームで業務を行なっています。 掲載されているお仕事の内容を見やすくしたり、応募しやすくなるようなアプリの改修や、 ストアにアプリのアップデートを反映させるリリース業務などを行なってきました。
1年目でも重要な機能の開発や、リリース業務に携わることが出来た事で、iOSだけでなく Androidも含めアプリ全体として考えられる視点を身に付ける事が出来たのは大きな学びの1つです。

そんな中で、技術的に印象に残っている事を紹介します。

A/Bテストの実装で...

アプリを改善していく中で、どちらの施策の方が効果的に改善できるかA/Bテストで判断することは多いかと思います。
A/BテストだとAパターンとBパターンの両方実装する必要があり、大変ですね。
抜け漏れも起こりやすくなってしまうと思います。

そんな時、例えばA/Bテストのパラメータなどを定義する場合
実装の仕方を工夫することを学びました。
swiftで値定義する際を例に示します。
2つの同数のパラメータを定義する場合

enum ABTestName: String {
      case testA = "normal"
      case testB = "changed"
}
enum ABTestId: String {
      case testA = "1234"
      case testB = "5678"
}

という書き方も出来ますが、ABTestIdとABTestNameが同数ならば
以下のような書き方も出来ます。

enum ABTestName: String {
    case testA = "normal"
    case testB = "changed"

    func ABTestId() -> String {
        switch self {
        case .testA:
            return "normal"
        case .testB:
        return "changed"
        }
    }
}

このように書いた場合、testBパターンのABTestIdを書き忘れてしまったとしても
switch文のお陰でコンパイルエラーとなり、書き忘れに気付くことが出来ます。
コードを書いていく段階でバグの原因を減らしていく事が
効率的にプロダクトを運用していく上で大切であることに気がつきました。
これはどんなプロダクトや言語にも通じる事だと思います。

1年目を終えて

入社して比較的すぐにメインプロダクトの開発に携わってきた中で
大規模なプロダクトを作っていく中での緊張感や大変なこともありますが、
改善していったアプリがユーザーの方々に使ってもらえて、良いレビューを頂けた時は
とても嬉しく思います。 まだまだ、技術者としては駆け出しですが、
より深く技術力をつけてアプリをグロース出来るように頑張ります!

@r-tsuzuki

こんにちは、@r-tsuzukiです。 19新卒で唯一の文系web開発未経験エンジニアとして入りました。 入社当初はGitHubの使い方やwebサービスの開発経験がなく、新卒研修で初めて本格的な開発をしました。

1年目の業務

開発演習を終えた後、求人媒体の一括管理サービスである「バイトルマスター」の開発チームに配属されました。 開発チームでは、企業の採用効率が良くするための機能や、一目で内容がわかるような画面を開発しています。

今回は、レスポンシブデザインの開発で学んだ話を共有します。

画面サイズが違っても、わかりやすくしたい

Webサービスは、ユーザーが自分にあった使い方ができると思います。 じっくり吟味したいときはパソコンの大画面で見比べたり、 簡単に確認したいときはスマートフォンで片手でさっと見たり...

画面サイズが変わったとしても、違和感なく、内容がわかるようにすることが大切だと思います。

例えば、パソコンでは違和感なく読める長い項目名が スマートフォンでは折り返したり、隣り合った項目を画面外に配置してしまったりしたら 見るためにスクロールしたり、画面を横向きにする必要が出てきてしまいます。

調べた結果、閲覧する画面サイズにあわせて表示を変える方法があることを知りました。

CSSの@media screen and (min-width: 画面の最小の幅)を使うと 同じ長い項目の表示でも、

パソコンの大画面で見た時→全部表示する スマートフォンで見た時→最初の数文字だけ表示する

という具合に分けて画面表示を変えられました。

それまで、端末毎に表示を変えられることを知らなかったので、 開発で使っている画面のみでの表示確認を行なっていましたが、 ブラウザ/画面毎での表示を確認するようになりました。

1年目を終えて

他にも、連携している求人媒体の取り込み処理や、深夜に動くバッチの改修も担当させていただきました。 当初開発経験がなく、何をしたらいいかわからなかった状態から フロントエンドからバックエンドまで幅広く経験を積むことで、 自分からどういう実装・仕様がいいかの意見を持てるようになりました。

まだまだエンジニアとして技術の理解や視野が狭い部分もあるので、 これからより使いやすいwebサービスを開発できるように頑張っていきたいと思います。

@r-kirishima

19新卒としてディップに入社した@r-kirishimaです。
大学は物理化学系で、情報系の知識はおおむね独学です。

開発研修後の本配属では「ナースではたらこ」のユーザサイトを中心に改修するチーム(以下ナースチーム)に所属しています。
ナースチームの特徴として様々な言語や技術を用いたシステムの保守・改修を上流工程からリリースまでがっつり関わることができる、というものがあります。
私自身、様々なプロジェクトへのチャレンジによって技術だけでなく、上流工程でのコミュ力や提案力も大きく伸ばすことができました。

具体的なお仕事の例

速度改善

ある時、企画部門から下記の要求を受けました。

速度改善ツールであるPageSpeed Insightsで[画像の遅延読み込み]をするべきという指摘をされているので対応して欲しい

しかし、対象のページはほとんど画像のないページであるため違和感を覚えました。
そこでフロントエンドを中心に改修を行っているチームと協力して調査を行ったところ、重たいのは確かに画像ではあったのですが、ページの下の方で用いられているGoogleMapの地図データでした。
地図データの受信メカニズムをChromeの開発者ツールNetworkを用いて調べたところ、通常の[画像の遅延読み込み]系の処理を組み込むだけでは読み込み時の地図データ受信を遅らせることはできないことがわかりました。 そこでGoogleMapを遅延読み込み化させられるライブラリlazyLoadGoogleMapsを今回は利用することにしました。

調査結果を元に改修方針を企画部門に相談し、無事実装を終えることができました。
その結果として、同サイトの他ページと比べてPageSpeed Insightsのスコアが低かったページを他ページと同等の水準まで引き上げることができました。
また、副次的な効果として画面内にGoogleMapの描画エリアが入るまでAPIを叩かない設定にしたのでAPIを叩く回数が減少し、サービスのコストダウンにもつなげることができました!

本プロジェクトを通しての個人の成長としては下記について学べました。

  • ブラウザのレンダリングの仕組み
  • 速度改善がサービスに及ぼす影響
  • 速度改善の具体的な手法
  • 遅延読み込み関連の実装法やテスト法

1年目を終えて

大きな裁量を持たせていただいた上、様々なチャレンジをさせてもらえたので、

  • プロジェクトを進めていく力
  • フロントからバックエンド、バッチ、DBなどの幅広い知識

などを身につけることができました。
とはいえ、まだまだ駆け出しで一つ一つの知識も浅いので、より深化させられるよう頑張っていきたいと思っています!

まとめ

GitHubや資料作成などの基本的なことから、実務で使用する様々な技術など学ぶことが多い1年間でした。 現状に満足することなく、今後も圧倒的成長をしていきます! (先輩方!引き続きご指導お願いします!)

最後まで読んでいただき、ありがとうございました。 少しでもディップに興味を持っていただけましたら、ぜひディップに遊びにきてください!

著者

dippeople.dip-net.jp dippeople.dip-net.jp dippeople.dip-net.jp

PHPの配列のソートまとめ

はじめに

こんにちは、2018年新卒で入社し、PHPで求人系サービスの開発や社内向けツールの開発を行なっている @taku-0728 です。
今回は私が公式ドキュメントにてPHPで配列をソートするやり方を調べようと思ったところ、色々な関数があって戸惑ったので全部自分なりにまとめてみました。
長くなってしまうため、みたい情報だけピックアップしてみていただければと思います。

関数一覧

解説

sort

まず最初に紹介するのはsortです。
sortは配列の値を基準として昇順にソートしますが、連想配列であってもキーと値の組み合わせは維持されません。

$fruitsLists = [];

$fruitsLists = ["d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple"];
sort($fruitsLists);
var_dump($fruitsLists);
array(4) {
  [0]=>
  string(5) "apple"
  [1]=>
  string(6) "banana"
  [2]=>
  string(5) "lemon"
  [3]=>
  string(6) "orange"
}

また、第2引数で比較条件を指定することもできます。
第2引数に指定可能な値は下記です。

  • SORT_REGULAR
    • デフォルトです。第2引数を省略した場合これが使われます。文字列の数値への変換が勝手に行われるため意図しないソート結果になることがあります。
    • 例えば下記の例だと同じ要素をソートしているにも関わらず結果が異なってしまいます。なんでこのような結果になるかは参考記事にも記載していますがこちらの記事がわかりやすいです。
      デフォルトですが極力使わずに、後述するSORT_NUMERICまたはSORT_STRINGのどちらかを使った方がいいと思います。
$array1=array("9", "1e1", "1f1");
sort($array1);
var_dump($array1);

$array2=array("1e1", "1f1", "9");
sort($array2);
var_dump($array2);
array(3) {
  [0]=>
  string(1) "9"
  [1]=>
  string(3) "1e1"
  [2]=>
  string(3) "1f1"
}
array(3) {
  [0]=>
  string(3) "1e1"
  [1]=>
  string(3) "1f1"
  [2]=>
  string(1) "9"
}
  • SORT_NUMERIC
    • 数字として項目を比較します。オプションを指定していない時と比較しても結果が異なっていないことが確認できます。
$array1=array("9", "1e1", "1f1");
sort($array1, SORT_NUMERIC);
var_dump($array1);

$array2=array("1e1", "1f1", "9");
sort($array2, SORT_NUMERIC);
var_dump($array2);
array(3) {
  [0]=>
  string(3) "1f1"
  [1]=>
  string(1) "9"
  [2]=>
  string(3) "1e1"
}
array(3) {
  [0]=>
  string(3) "1f1"
  [1]=>
  string(1) "9"
  [2]=>
  string(3) "1e1"
}
  • SORT_STRING
    • 文字列として項目を比較します。こちらもオプションを指定していない時と比較しても結果が異なっていないことが確認できます。
$array1=array("9", "1e1", "1f1");
sort($array1, SORT_STRING);
var_dump($array1);

$array2=array("1e1", "1f1", "9");
sort($array2, SORT_STRING);
var_dump($array2);
array(3) {
  [0]=>
  string(3) "1e1"
  [1]=>
  string(3) "1f1"
  [2]=>
  string(1) "9"
}
array(3) {
  [0]=>
  string(3) "1e1"
  [1]=>
  string(3) "1f1"
  [2]=>
  string(1) "9"
}
  • SORT_LOCALE_STRING
    • 現在のロケールに基づいて比較します。setlocale()でロケールを指定します。
// ロケールをオランダ語に設定
setlocale(LC_ALL, 'nl_NL');

// オランダ語が入った配列をソート
sort($netherLandsArray, SORT_LOCALE_STRING);
  • SORT_NATURAL
    • 項目の比較を文字列として行い、 後述のnatsort() と同様の「自然順」で比較します。
$array1 =  array("img12.png", "img10.png", "img2.png", "img1.png");
$array2 = $array1;

sort($array1);
var_dump('asortの実行結果');
var_dump($array1);

sort($array2, SORT_NATURAL);
var_dump('natsortの実行結果');
var_dump($array2);
string(19) "sortの実行結果"
array(4) {
  [0]=>
  string(8) "img1.png"
  [1]=>
  string(9) "img10.png"
  [2]=>
  string(9) "img12.png"
  [3]=>
  string(8) "img2.png"
}
string(27) "SORT_NATURALの実行結果"
array(4) {
  [0]=>
  string(8) "img1.png"
  [1]=>
  string(8) "img2.png"
  [2]=>
  string(9) "img10.png"
  [3]=>
  string(9) "img12.png"
}
  • SORT_FLAG_CASE
    • 文字列の比較で大文字小文字を区別しないようにします。SORT_STRINGやSORT_NATURALとOR条件(|)で組み合わせて使います。
$array1=array("aaa", "bbb", "AAA", "BBB");
sort($array1);
var_dump('sortの実行結果');
var_dump($array1);

$array2=array("aaa", "bbb", "AAA", "BBB");
sort($array2, SORT_FLAG_CASE|SORT_STRING);
var_dump('SORT_FLAG_CASE|SORT_STRINGの実行結果');
var_dump($array2);
string(19) "sortの実行結果"
array(4) {
  [0]=>
  string(3) "AAA"
  [1]=>
  string(3) "BBB"
  [2]=>
  string(3) "aaa"
  [3]=>
  string(3) "bbb"
}
string(41) "SORT_FLAG_CASE|SORT_STRINGの実行結果"
array(4) {
  [0]=>
  string(3) "aaa"
  [1]=>
  string(3) "AAA"
  [2]=>
  string(3) "bbb"
  [3]=>
  string(3) "BBB"
}

rsort

sortの逆で、値を基準として降順にソートします。
キーと値の組み合わせが維持されない点と、第2引数でオプションが指定できる点も同様です。

$fruitsLists = [];

$fruitsLists = ["d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple"];
rsort($fruitsLists);
var_dump($fruitsLists);
array(4) {
  [0]=>
  string(6) "orange"
  [1]=>
  string(5) "lemon"
  [2]=>
  string(6) "banana"
  [3]=>
  string(5) "apple"
}

asort

次に紹介するのはasortです。
asortは配列の値を基準として昇順にソートします。
sortと同様に第2引数でオプションを指定することもできます。
sortとの違いはキーと値の組み合わせは維持される点です。

$fruitsLists = [];

$fruitsLists = ["d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple"];
asort($fruitsLists);
var_dump($fruitsLists);
array(4) {
  ["c"]=>
  string(5) "apple"
  ["b"]=>
  string(6) "banana"
  ["d"]=>
  string(5) "lemon"
  ["a"]=>
  string(6) "orange"
}

arsort

arsortはasortの逆で値を基準として降順にソートします。

$fruitsLists = [];

$fruitsLists = ["d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple"];
arsort($fruitsLists);
var_dump($fruitsLists);
array(4) {
  ["a"]=>
  string(6) "orange"
  ["d"]=>
  string(5) "lemon"
  ["b"]=>
  string(6) "banana"
  ["c"]=>
  string(5) "apple"
}

ksort

ksortは配列のキーを基準として昇順にソートします。
asortが配列の値を基準としていたのに対し、こちらはキーを基準としています。
sortと同様に第2引数でオプションを指定することもできます。

$fruitsLists = [];

$fruitsLists = ["d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple"];
ksort($fruitsLists);
var_dump($fruitsLists);
array(4) {
  ["a"]=>
  string(6) "orange"
  ["b"]=>
  string(6) "banana"
  ["c"]=>
  string(5) "apple"
  ["d"]=>
  string(5) "lemon"
}

krsort

krsortはksortの逆で配列のキーを基準として降順にソートします。

$fruitsLists = [];

$fruitsLists = ["d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple"];
krsort($fruitsLists);
var_dump($fruitsLists);
array(4) {
  ["d"]=>
  string(5) "lemon"
  ["c"]=>
  string(5) "apple"
  ["b"]=>
  string(6) "banana"
  ["a"]=>
  string(6) "orange"
}

usort

usortはユーザー定義の比較関数により配列をその値でソートします。
つまり、コールバック関数を使って自分が指定した順番でソートできるということです。

function sortStrlen($a, $b) {
    // 同じ長さの場合には、順序を変えない
    if (strlen($a) === strlen($b)) {
        return 0;
    }
    // $aの方が短い場合には、$aの順番を$bより繰り下げる
    // $aの方が長い場合には、$aの順番を$bより繰り上げる
    return (strlen($a) < strlen($b)) ? -1 : 1;
};

$fruitsLists = [];

$fruitsLists = ["d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple"];
usort($fruitsLists, 'sortStrlen');
var_dump($fruitsLists);
array(4) {
  [0]=>
  string(5) "lemon"
  [1]=>
  string(5) "apple"
  [2]=>
  string(6) "orange"
  [3]=>
  string(6) "banana"
}

uasort

uasortはusortと似ていますが、キーと値の組み合わせを保持する点に違いがあります。

function sortStrlen($a, $b) {
    // 同じ長さの場合には、順序を変えない
    if (strlen($a) === strlen($b)) {
        return 0;
    }
    // $aの方が短い場合には、$aの順番を$bより繰り下げる
    // $aの方が長い場合には、$aの順番を$bより繰り上げる
    return (strlen($a) < strlen($b)) ? -1 : 1;
};

$fruitsLists = [];

$fruitsLists = ["d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple"];
uasort($fruitsLists, 'sortStrlen');
var_dump($fruitsLists);
array(4) {
  ["d"]=>
  string(5) "lemon"
  ["c"]=>
  string(5) "apple"
  ["a"]=>
  string(6) "orange"
  ["b"]=>
  string(6) "banana"
}

uksort

usortが配列の値を使って項目の比較を行なっていたのに対し、uksortは配列のキーを使って項目の比較を行います。

function sortStrlen($a, $b) {
    // 同じ長さの場合には、順序を変えない
    if (strlen($a) === strlen($b)) {
        return 0;
    }
    // $aの方が短い場合には、$aの順番を$bより繰り下げる
    // $aの方が長い場合には、$aの順番を$bより繰り上げる
    return (strlen($a) < strlen($b)) ? -1 : 1;
};

$fruitsLists = [];

$fruitsLists = ["aa" => "lemon", "aaa" => "orange", "a" => "banana", "aaaa" => "apple"];
uksort($fruitsLists, 'sortStrlen');
var_dump($fruitsLists);
array(4) {
  ["a"]=>
  string(6) "banana"
  ["aa"]=>
  string(5) "lemon"
  ["aaa"]=>
  string(6) "orange"
  ["aaaa"]=>
  string(5) "apple"
}

natsort

natsortはSORT_NATURALオプションを使用したときと同じように「自然順」アルゴリズムでキーと値の組み合わせを保持したまま配列をソートします。
わかりやすいようにasortと実行結果を比較してみます。

$array1 =  array("img12.png", "img10.png", "img2.png", "img1.png");
$array2 = $array1;

asort($array1);
var_dump('asortの実行結果');
var_dump($array1);

natsort($array2);
var_dump('natsortの実行結果');
var_dump($array2);
string(20) "asortの実行結果"
array(4) {
  [3]=>
  string(8) "img1.png"
  [1]=>
  string(9) "img10.png"
  [0]=>
  string(9) "img12.png"
  [2]=>
  string(8) "img2.png"
}
string(22) "natsortの実行結果"
array(4) {
  [3]=>
  string(8) "img1.png"
  [2]=>
  string(8) "img2.png"
  [1]=>
  string(9) "img10.png"
  [0]=>
  string(9) "img12.png"
}

asortでソートすると、2よりも1の方が数字として若いため、2より10や12が先になります。
それに対してnatsortでソートすると自然な数字の順番になっています。

natcasesort

natcasesortはnatsortと似ていますが大文字小文字を区別しない点に違いがあります。

$array1 =  array('IMG0.png', 'img12.png', 'img10.png', 'img2.png', 'img1.png', 'IMG3.png');
$array2 = $array1;

asort($array1);
var_dump('asortの実行結果');
var_dump($array1);

natcasesort($array2);
var_dump('natcasesortの実行結果');
var_dump($array2);
string(20) "asortの実行結果"
array(6) {
  [0]=>
  string(8) "IMG0.png"
  [5]=>
  string(8) "IMG3.png"
  [4]=>
  string(8) "img1.png"
  [2]=>
  string(9) "img10.png"
  [1]=>
  string(9) "img12.png"
  [3]=>
  string(8) "img2.png"
}
string(26) "natcasesortの実行結果"
array(6) {
  [0]=>
  string(8) "IMG0.png"
  [4]=>
  string(8) "img1.png"
  [3]=>
  string(8) "img2.png"
  [5]=>
  string(8) "IMG3.png"
  [2]=>
  string(9) "img10.png"
  [1]=>
  string(9) "img12.png"
}

array_multisort

array_multisortは主に多次元配列や複数の配列を一度にソートしたいときに使われます。
下記の例で果物の配列を名前の昇順かつ、価格の昇順になるようにソートしてみます。

$fluitsLists = [
        ['name' =>'lemon','price' =>200],
        ['name' =>'orange','price' =>100],
        ['name' =>'banana','price' =>200],
        ['name' =>'apple','price' =>300],
    ];

// ソートの基準となる「name」と「price」を配列に入れる
foreach( $fluitsLists as $fluits) {
    $nameLists[] = $fluits['name'];
    $priceLists[] = $fluits['price'];
}

// ソート前の配列を出力
var_dump('array_multisortの実行前');
var_dump($fluitsLists);

// ソート実行
array_multisort($nameLists, SORT_ASC, SORT_STRING, $priceLists, SORT_ASC, SORT_NUMERIC, $fluitsLists);

// ソート後の配列を出力
var_dump('array_multisortの実行後');
var_dump($fluitsLists);
string(27) "array_multisortの実行前"
array(4) {
  [0]=>
  array(2) {
    ["name"]=>
    string(5) "lemon"
    ["price"]=>
    int(200)
  }
  [1]=>
  array(2) {
    ["name"]=>
    string(6) "orange"
    ["price"]=>
    int(100)
  }
  [2]=>
  array(2) {
    ["name"]=>
    string(6) "banana"
    ["price"]=>
    int(200)
  }
  [3]=>
  array(2) {
    ["name"]=>
    string(5) "apple"
    ["price"]=>
    int(300)
  }
}
string(27) "array_multisortの実行後"
array(4) {
  [0]=>
  array(2) {
    ["name"]=>
    string(5) "apple"
    ["price"]=>
    int(300)
  }
  [1]=>
  array(2) {
    ["name"]=>
    string(6) "banana"
    ["price"]=>
    int(200)
  }
  [2]=>
  array(2) {
    ["name"]=>
    string(5) "lemon"
    ["price"]=>
    int(200)
  }
  [3]=>
  array(2) {
    ["name"]=>
    string(6) "orange"
    ["price"]=>
    int(100)
  }
}

結果を見ると名前の昇順かつ、価格の昇順になっていることがわかると思います。

shuffle

最後にソートとは少し違いますが、shuffleも紹介しておきます。
名前の通り、配列の順番をランダムにします。
この時キーと値の組み合わせが維持されない点に注意が必要です。

$fruitsLists = [];

$fruitsLists = ["d" => "lemon", "a" => "orange", "b" => "banana", "c" => "apple"];
shuffle($fruitsLists);
var_dump($fruitsLists);
array(4) {
  [0]=>
  string(6) "orange"
  [1]=>
  string(5) "apple"
  [2]=>
  string(6) "banana"
  [3]=>
  string(5) "lemon"
}

何度か実行したらわかりますが、実行するたびに結果が変わります。

まとめ

PHPで配列をソートするやり方について解説しました。
最近では並び替えなどはRDBで行うのが一般的であり、PHPで配列の並び替えを行う機会など多くはないかもしれませんが、いざという時のために知っておくと便利かなと思います。
最後までお付き合いいただきありがとうございました。

参考

配列のソート
【 php 】usort関数の使い方
多次元配列のソート:array_multisort関数
PHPのsort関数は相当おかしい

筆者

筆者 (写真右)

XCodeGen対応のTIPS

はじめに

こんにちは。iOSエンジニアの@satoshi-babaです。 いま巷で賑わっているXCodeGenを検証する過程で、ハマりどころがいっぱいあったので簡単なTIPSを共有したいと思います。 そもそもXCodeGenがどんなものかというのは割愛させていただきます。

バイトルはなぜXCodeGen対応したの?

バイトルiOSは10名前後のエンジニアが開発に携わっています。 ABテストの実施もよく行っており、ファイルの追加/削除が頻繁に行われるようになっています。

元々、開発フローの課題の1つにxcodeprojのコンフリクト抑止がありました。 その中で年末に行われていたtry! Swift Tokyo 2020 Meetup!!に参加した時に、XCodeGenの紹介があり「これだ!」と思い導入することにしました。

TIPS

Templateバリ便利!

SchemeとTargetにそれぞれTemplateを用意することができます。

例えばTargetであればこんな感じにテンプレートを用意することで記載をかなり簡略がすることができます。 例えば複数の環境設定がされている場合に使えます。

targetTemplates:
  application-base:
    type: application
    platform: iOS
    sources:
      - path: xxxxx
    info:
      properties:
        UISupportedInterfaceOrientations: [UIInterfaceOrientationPortrait]
        UILaunchStoryboardName: LaunchScreen
    settings:
      base:
        TARGETED_DEVICE_FAMILY: 1
        CODE_SIGN_STYLE: Manual
        ENABLE_BITCODE: No
        GCC_C_LANGUAGE_STANDARD: gnu99
        CLANG_CXX_LANGUAGE_STANDARD: gnu++0x
        CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED: Yes
    dependencies:
      - sdk: SystemConfiguration.framework
      - sdk: libsqlite3.tbd
    preBuildScripts:
      - name: R.Swift
        script: |
          echo 'make R.generated.swift'
          if [ -e "$SRCROOT/xxxxx/xxxxx/R.generated.swift" ]; then
          echo 'remove R.generated.swift'
          rm "$SRCROOT/xxxxx/xxxxx/R.generated.swift"
          fi
          "$PODS_ROOT/R.swift/rswift" generate "$SRCROOT/xxxxx/xxxxx/R.generated.swift"
        inputFiles:
          - $TEMP_DIR/rswift-lastrun
        outputFiles:
          - $SRCROOT/xxxxx/xxxxx/R.generated.swift
targets:
  # 個別の微小な設定を書くだけだから楽できる。
  stage1:
    templates:
      - application-base
    info:
      path: $(SRCROOT)/xxxxx/xxxxx/stage1/Info.plist
  stage2:
    templates:
      - application-base
    info:
      path: $(SRCROOT)/xxxxx/xxxxx/stage2/Info.plist

R.generated.swiftが解決できない時の対処

Gitからcloneした直後にxcodegenしてビルドすると以下のエラーが発生します。

``

R.swiftを導入したことがある方なら気づいていると思いますが、Run ScriptでR.generated.swiftを作成した後に手動で追加する手順があります。 そうするとxcodegenでプロジェクトファイルを生成する時には、上記のファイルはないわけなので参照に含まれなくなります。

そんな時はsourcesの設定でファイルがないときでも参照を追加するようにしましょう!

sources:
  - path: xxxxx
  - path: xxxxx/xxxxx/R.generated.swift
    optional: true
    createIntermediateGroups: true

Build Settingsのキー名の探し方

Build Settingsで細かい設定がされている場合、XCodeGenのsettingsで設定する必要があります。 バイトルの場合はC++ Language Dialectが該当していて設定する必要がありました。

こんな感じに設定します。

settings:
  base:
    CLANG_CXX_LANGUAGE_STANDARD: gnu++0x

この設定のキー名はCode Sign Styleなんかはなんとなく勘で行けることもありますが、 物によっては全くわからない場合があります。

そんな時はXCodeのプロジェクト設定から探すことができます。(Appleにドキュメントがあると思いますが探してません) 画像のQuick HelpのDeclarationを参照すれば一発ですね!これで大概のことはできそうです!

f:id:satoshi-baba:20200327095223p:plain

Build Settingsの設定値の探し方

Build Settingsのキーはわかっても具体的な設定がわからないことがあります。 例えばバイトルでハマったのは、C++ Language Dialectの設定値がわかりませんでした。

設定値は具体的な何を設定する項目なのかがわかれば、各種ドキュメントから漁ることで解決できます。 C++ Language DialectはClangの設定項目なので、 このページを参照することで解決することができました。

まとめ

XCodeGenのハマりどころは多いですがキチンと対処がわかっていれば怖くありません。 バイトルも取り入れられるところはどんどんモダンな技術を取り入れていきたいと思います。

著者

dippeople.dip-net.jp

Bridging HeaderをはずしたらUIKitが解決できなくなった!

はじめに

こんにちは。iOSエンジニアの@satoshi-babaです。 いま巷で賑わっているXCodeGenを検証する過程で、UIKit系のクラスの参照が解決できない事象に出くわしました...。 その事象を解決するまでの道のりを共有したいと思います。

事の始まり

現在、私はXCodeGenの検証を実施しています。

ある日私はほとんどの設定を書き終え「いや〜これは完璧だわ!ビルドして動作確認するぞ!」と息巻いていたところ、 およそ1000を超えるSwift Compiler Errorに遭遇してしまいました。

表示されていたエラーは大きく分けて以下の3つでした。

  • Use of Undeclared Identifier CGFloat
  • Use of Undeclared Identifier UIFont
  • Use of Undeclared Identifier UIImage

該当の処理を確認すると、確かにCGFloatやUIFontは使用されています。 しかしimportされているのはFoundationのみで、UIKitに属するCGFloatやUIFontは解決できなそうです。

import Foundation
extension String {
    func labelHeight(width: CGFloat, font: UIFont, maxLine: Int = 0) -> CGFloat {
        let label = UILabel()
        label.font = font
        label.lineBreakMode = .byTruncatingTail
        label.numberOfLines = maxLine
        label.text = self
        let rect: CGSize = label.sizeThatFits(CGSize(width: width-label.layoutMargins.left-label.layoutMargins.right, height: CGFloat.greatestFiniteMagnitude))
        return rect.height
    }
}

とりあえず全てのクラスにUIKitのimportすれば動きそうです。 しかし、なぜこのような状態で今までコンパイルできたのか、XCogeGenで生成したプロジェクトで解決できなくなったのか気になります。 もしかしたら重要な見落としがあるかと思い、調査することにしました。

原因はBridging Header忘れ!!

XCogeGenで生成したプロジェクトの時に発生する問題です。絶対に設定し忘れている項目があるはずです! Build Settingsを上から順番チェックしてみることにしました。

すると見事にBridging Headerの追加を忘れていました! f:id:satoshi-baba:20200213142343p:plain

設定を追加したら無事にビルドができるようになりました。 これで安心!...では終われません!

Bridging Headerの動きをさらに追ってみることにしました。

Bridging Headerは全てのSwiftファイルにimportされる

まずはBridging Headerの動作について、下記の公式ページで確認してみました。 Importing Objective-C into Swift

どうやらBridging Headerは全てのSwiftファイルにimportされるみたいです。 Bridging Headerまたはその先でUIKitをimportしていればいい感じに解決されちゃいそうですね...!

実際に読み込んでいる先でimportされていました!

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface HogeLogic : NSObject
(省略)

感想

Bridging Headerをはずすだけでこんな影響が出るなんて思いもしませんでした。 昔のC言語でのCommonヘッダーみたいな役割が簡単にできちゃいますね!(意図してませんでしたが) もしかしたらコードレビューの観点に盛り込むべきかもしれませんね。

著者

dippeople.dip-net.jp

SRE NEXT2020参加レポート

SRE NEXT 2020行ってきました

こんにちはSREエンジニアやってます、@hayaosatoです。

1/25(土)に開催されたSRE NEXT 2020へ参加してきたのでそのレポート記事を書かせていただきます。

本イベントは弊社もブロンズスポンサーとして協賛させていただき、会場でもノベルティが配られてました!

写真は弊社のポスターとステッカー、そしてSRE NEXTのトートバッグです!

f:id:hayaosato:20200131104622j:plain

また、当日スタッフとして弊社のおざけんがカメラマンとして参加、

コアスタッフとして@bayashi_okさんがホール統括を行なっていました!!

発表紹介

SRE NEXT 2020の中で個人的に特に印象の残った発表について、いくつか感想を書かせていただきます。

分散アプリケーションの信頼性観測技術に関する研究

資料: https://speakerdeck.com/yuukit/a-study-of-sre

SREの今後についてやそれに付随する研究などをとても丁寧に説明されていたSRE NEXTらしい発表でした。

SREがなんであるかのゆううきさんなりの定義は

サイト信頼性を制御するための工学であり、

自動化して全て解決というわけではなく、認知負荷が高まってしまう 失敗を許容する前提で運用を設計することで変更速度を最大化する。

とのこと。

確かに自動化するだけだと認知負荷が高まってしまい、 システムを構築した人しか認知できなくなる。それが原因で属人化してしまうこともある得るのかなと思いました。

また、失敗を許容する前提での運用設計は今後我々もやっていきたいなと思いました。

成長を続ける広告配信プラットフォームのモニタリングを改善してきた話

資料: https://speakerdeck.com/larufa/cheng-chang-wosok-keruguang-gao-pei-xin-puratutohuomufalsemonitaringuwogai-shan-sitekitahua

やりがちな監視の例などを踏まえて監視における課題やその解決方法まで非常に丁寧でわかりやすく説明されていた発表でした。

また、アラートの整理する(洗練などの)タイミングについて、アラートの整理は定期的にやる事は確かに難しく、障害が実際に起きた時や新しいメンバーがジョインしたタイミングでアラートの整理をやっているそうです。

確かにそれらのタイミングだと、アラート整理に工数を確保しやすいなと感じ、今後我々もそのようなタイミングでアラートの整理を行なっていきたいなと思いました。

ZOZO MLOps のチームリーディングとSRE(Engineering)

資料: https://docs.google.com/presentation/d/1zEkR9Dm_epg7fxOCFE-asBsUlHDozwObsBEGAILiqic/edit

ML機能を世に出しすための取り組みの、エンジニアリングにおけるチームリーディングについての発表でした。

現在複数のML機能を世に出した段階で、MLOpsチームとしてもっと多くのML機能を世に出してML機能に対する信頼度を高めようとしているフェーズだそうです。

このようにMLOpsチームの目標が明確で、さらにプロジェクト進行がスムーズに進むようにはどうすべきか。

リーダーがボトルネックとならないためにはどのようにすべきか。など組織開発として非常に参考になる発表でした。

特に、最終的には技術でぶん殴るという文化は非常に共感し、我々もぶん殴れるほどの技術力をつけていかなければなと思いました。

Webサービスを1日10回デプロイするための取り組み

資料: https://speakerdeck.com/fujiwara3/sre-next-2020

デプロイ頻度をあげてデプロイへの恐怖を減らための取り組みでした。

誰でもデプロイ/ロールバックできるようにするためのデプロイ構成についてとても具体的に説明されていた発表でした。

特に、CircleCI使用時のECRのリージョンなど、小ネタ等もとても面白かったです。

雑感

SRE NEXT 2020、全体的に登壇者のレベルが非常に高くSREにおけるベストプラクティスや考え方の共有をしていただき、非常に学びの多い勉強会でした。

また、SREとしての対策が新規メンバーの育成やチームマネジメント的な話にも派生していたりと、幅広い知見が得られました。

SREの今後として、SREはインフラからアプリケーションまで全てのレイヤで開発されるようになり、SREを考えることが当たり前になっていくと良いなと思いました。

筆者

dippeople.dip-net.jp (写真左)

2019年アドベントカレンダーまとめ

はじめに

こんにちは、2018年新卒で入社し、求人系サービスの開発や社内向けツールの開発を行なっている @taku-0728 です。
今回は12/1~12/25に有志で行なったディップ Advent Calendar 2019について書きたいと思います!

Advent Calendar とは

一般的なアドベントカレンダーはクリスマスまでの期間に日数を数えるために使用されるカレンダーのことを指しますが、エンジニアの中では個人または複数人で記事を書いてクリスマス当日までバトンをつなげていく文化のことを指します!
弊社は2016年から毎年 Qiita Advent Calendar に参加しています。任意で参加者を募り、業務に関係するものから自分で興味を持った技術まで自由なテーマで記事を書いています。
今回はそのアドベントカレンダーの中から私が気になった記事を中心に振り返ります!

おすすめ記事一覧

Pegmatiteが素晴らしいツールなので自分好み(GitHub Enterprise Serverで使えるよう)にしてみた

Pegmatiteが素晴らしいツールなので自分好み(GitHub Enterprise Serverで使えるよう)にしてみた
PegmatiteというPlantUMLを画像化するGoogle Chromeの機能拡張を弊社のGitHub Enterprise Serverで使えるようにした話です。
著者の方でPRを出したところ、マージしていただき無事リリースされたそうです。

なぜコンテナを導入するのか ~ 各職種への説得材料をまとめる

なぜコンテナを導入するのか ~ 各職種への説得材料をまとめる
Dockerコンテナに触ったことがない人に対して、Dockerコンテナ利用時のメリットを説明している記事です。
サービス開発者、インフラ担当、セキュリティ担当それぞれの目線でメリットを説明しているためどの立場からみてもわかりやすい記事になっています。
著者は定期的にDockerコンテナに関する社内勉強会を開催しています。

日報がTILだったお陰でGithubとMarkdownがわかった話

日報がTILだったお陰でGithubとMarkdownがわかった話
2019年新卒入社した文系出身のエンジニアが毎営業日つけている日報によってGithubの仕組みやMarkdownの書き方を身につけた話です。
著者と同じように文系出身でGithubの仕組みなどがいまいちわかっていない方におすすめの記事だと思います。

バイトル(iOS)のCI/CDを大公開!!

バイトル(iOS)のCI/CDを大公開!!
弊社で(おそらく)もっとも有名なサービス、バイトルスマホアプリ(iOS)のCI/CDがどうなっているかわかってしまうレアな記事です。
読むとバイトルスマホアプリリリースの裏側が知れるかも。

若手エンジニアが学んだ工数見積もり方法

若手エンジニアが学んだ工数見積もり方法
最後は私 @taku-0728 が書いた記事を紹介します!
元々工数見積もりが苦手だった私が先輩にやり方を教わって、実践してみてどういう学びがあったのかをまとめています。
同じように工数見積もりが苦手な方に読んでいただければと思います。

おまけ

上記とは別に、1人で25記事全部埋める「全部俺カレンダー」に挑戦した猛者が何人かいます。
よければそちらも目を通していただければと思います。

また、過去実施したアドベントカレンダーはこちらから確認できます。

最後に

今回は12月に行なったアドベントカレンダーについて紹介しました。
弊社ではアドベントカレンダー以外にも様々な試み、イベントを行なっています。
適宜ブログにて発信していくので楽しみにお待ちください。

また、弊社では一緒にサービスを開発している仲間を募集しています!
とりあえず話だけ聞きたいでも構いません。興味がある方は下記よりご連絡ください!

www.dip-net.co.jp

筆者

dippeople.dip-net.jp (写真右)

ブログリニューアルしました!

はじめに

こんにちは! ディップ株式会社でエンジニアをやっている@satoshi-babaと申します。

この度、弊社のエンジニアブログをリニューアルいたしました。 ここではリニューアルの目的などを説明させていただこうと思います!

リニューアルの目的

弊社は様々なイベントに登壇したりしていますが、知名度としてはまだまだイマイチです。 イベントで「ディップさんってエンジニアいるんですね」と言われることもいまだにあるんですよね。

そこで、エンジニアブログをリニューアルし、我々が技術に対して真摯に向き合っていることをご理解いただき、弊社開発部の魅力を知っていただきたいと考えました!

投稿内容について

弊社はバイトルを始めとし、中〜大規模サービスを運営しています。 ここではサービスに使用している技術やマネジメント術などを積極的に公開していきます。 そのほかに、イベント情報やエンジニアが個人的に気になっている話なども取りあげたいと思います。

早速来週から、記事を上げ始める予定となっております。 「読者になる」ボタンを押していただき、楽しみにお待ちください!

おわりに

惜しみなくアウトプットをしていきたいと思いますので、どうぞよろしくお願いいたします。

また、弊社では一緒にサービスを開発している仲間を募集しています! とりあえず話だけ聞きたいでも構いません。興味がある方は下記よりご連絡ください!

www.wantedly.com

著者

dippeople.dip-net.jp