2015/05/20

フリーランスになって三ヶ月後の振り返り


フリーランスとして活動し始めて(2015年2月〜)、三ヶ月が経ちました。
制作実績として記しておきます。
中には秘密保持契約の関係で記載していないものもあります。

筆者背景

1987年生まれ、関西在住。
私立文系大学卒業後、Windowsパッケージソフト開発会社、WEBシステム開発会社を経て、
2015年2月よりフリーランス(個人事業主)、WEB、アプリ開発者として活動
プロフィールサイトはこちら
http://manchan.github.io/index.html

5月

  • AppleWatchアプリ開発(Swift)https://yourjam.es
  • Androidアプリ開発(Java)
  • Javascriptデバッグスクリプト改修案件
  • 資料作成,サンプルAPI作成(Amazon Machine Learning)今ホットな機械学習案件
  • 既存サイト修正案件(PHP)

4月

  • AppleWatchアプリ開発(Swift)
    付随でApple日本法人に出向く(Watchkit lab(Apple Watch発売前、実機検証))Apple信者なのでAppleのエンジニアの方と話が出来て、夢のような体験でした。
  • 大手製薬企業、営業向けiPadアプリ(HTML5, CSS3, Backbone.js, jQuery)
  • iOSアプリ改修(Objective-C)
  • ワードプレス改修、サーバ移行案件

3月

  • 大手製薬企業向けiPadアプリ(HTML5, CSS3, Backbone.js, jQuery)
  • 自動投稿システム開発(PHP)

2月

  • 自動投稿システム開発(PHP)
  • HTML5+JavaScriptによるD&Dコンテンツ開発
  • 既存サイト、ワードプレス化案件

振り返り

振り返ってみると、まぁまぁかなといったところ。
おもしろいことにフリーになったと発信すると、意外や意外、声をかえてくださったり、
仕事をくれたり、飲みに誘ってもらったり、ありがたい限りです。
4月にAppleの日本支社に行けたことがすごく刺激になった。忘れられない思い出。
個人でぼちぼちやってるレベルなので、自分が動けば動く程、良い結果が待っていると考えて、もっと切り詰めてやるべき。
もっと深くクライアントにヒアリングしたり、求めていることを具現化するべき。
継続的にお仕事いただけるところもあれば、単発で終わるところもある。
手広くなんでもやっているので、もう少しiOSアプリ中心にやっていきたい。
直近でサンプルレベルですが初めてAndroidアプリに着手しました。
iOSと違って申請後、公開まで2,3時間なのは、魅力的。
やらず嫌いはよくない。

困ったこと

契約不履行は普通にある。
実際、報酬未払いによる集団訴訟がらみのトラブルを経験したが、自分は少額だったので、勉強代と考える。
なんでもかんでもできる事で、仕事を受ければいいというものではない。
クライアントをよく判断する。

今後の目標

  • 興味、関心があるプロジェクトに参画し、それをお金に変える(利益を求め過ぎない)
  • オープンソースに貢献する(githubのstar100以上)
  • 役に立ってかつ、ひとひねりある、そんなサービスを企画、開発する(アイディア or サンプルはあるがリリースまでもっていけてない、、、)
  • 来年はWWDCに行く
  • 仕事がらみで海外に行く
  • ブログの更新頻度をあげる(最低週一)

おわりに

上記目標を今後一、二年で達成できるようにする。
まだまだこれからです。

2015/05/04

lodash(Underscore.js)まとめ, Swift, Objective-C, PHPもあるよ

lodash(Underscore.js)

JavaScriptのユーティリティライブラリ。
2015年5月現在、npmでもっとも多く参照されている。
https://www.npmjs.com/browse/depended
lodashはUnderscoreの派生ライブラリ。
Backbone.jsは、もともとunderscore.jsに依存していますし、
lodashは、JavaScriptトランスパイラのBabel,ブログプラットフォームのGhost,
プロジェクトのベースとなるツールであるYeomanなど,npmの主要なパッケージのいくつかは依存している。
AngularJsでDI(依存性注入)してlodash(underscore.js)をモジュール化して、そのまま使用してもいい。
Reactでももちろん使用可能。

lodash(Underscore.js)を使用する目的

配列やオブジェクトの操作をする際に便利
コードの可読性アップ
つまりコードをシンプルかつ短縮できる
for文、if文を多用せずに済む
つまり保守性が高い(変数の状態を知らなくて済む, 関数は参照透過性が保たれている)

Underscoreとlodashの比較

Lo-Dashの方が機能数が多い
Lo-Dashの方が高速、パフォーマンスがいい
native vs. array.js vs. underscore vs lo-dash
http://jsperf.com/native-vs-array-js-vs-underscore/8

lodash(Underscore)便利メソッド集

// latest 3.7.0
var _ = require('lodash');

/**
 * lodash(Underscore.js) の便利な関数ライブラリたち
 * 「_」というオブジェクトを使用することにより利用可能
 * 配列、オブジェクト操作を便利にするもの
 *
 * よく使うのは以下
 * each
 * find
 * filter
 * where
 * findWhere
 * contains
 * pluck
 * max
 * min
 * sortBy
 */

Collection functions

each

/**
 * ループ処理 collection each
 * => each: 4
 * => each: 10
 * => each: 16
 */
_.each([2, 5, 8], function(num){
    console.log( 'each: ' + num * 2 );
});

for と mapの例

/**
 * jsのfor文で配列に要素を追加
 * => count_arr:2,4,6,8,10
 */
var count_arr = [];
for(var i = 1; i < 6; i++){
    count_arr.push(i * 2); // マッピングのたびに外部の変数の状態が変化する ☓
}
console.log('count_arr:' + count_arr);

/**
 * mapで配列を操作
 * 返り値は配列
 * => map: 2,4,6,8,10
 */
var map = _.map(_.range(1, 6), function(num){
    return num * 2; // 変数の状態を考えずに済む ○
});
console.log( 'map: ' + map );

for と reduce × filterの例

/**
 * 文字列配列の内、三文字以上のものを選択し、
 * 文字数の合計を集計する
 * => 18
 */
var cities = ["京都", "サンクトペテルブルク", "ボゴダ", "東京", "ゴア", "バルセロナ"];
var total_str_len = 0;
for(var city in cities){
    var str_len = cities[city].length;
    if(str_len >= 3){
        total_str_len += str_len; // マッピングのたびに外部の変数の状態が変化する ☓
    }
}
console.log( 'for total_str_len:' + total_str_len); // => 18


/**
 * 上記をreduce × filter でやる
 * reduceの返り値は単一の値
 * filterにて、条件で絞った配列に対して, reduceで一つずつの要素に対して処理をする。
 * memoに結果がストックされる。第三引数0はmemoの初期値
 * => 18
 */
var total_len = _.reduce(
    _.filter(cities,function(city){return city.length >= 3;}),
    function(memo, str){ return memo + str.length;}, 0);
console.log( 'function total_len:' + total_len); // 18

chain

/**
 * chain メソッドチェーンで使用
 * value()でchain結果を取得
 * => 18
 */
var total = _.chain(cities)
    .filter(function(city){ return city.length >= 3 })
    .reduce(function(memo, str){ return memo + str.length;}, 0)
    .value();
console.log( 'function total:' + total); // 18

flatMapを使用したい場合(カスタム)

/**
 * flatMapを使用したい場合
 */
var flatMap = _.flatMap = _.concatMap = function (xs, f, self) {
        return _.reduce(xs, function (ys, x) {
            return ys.concat(f.call(self, x));
        }, []);
    };
_.each(['flatMap', 'concatMap'], function (name) {
    _.prototype[name] = function (f, self) {
        return _(flatMap(this.value(), f, self));
    };
});


/**
 * 以下、
 * 整数リストのリスト中から奇数だけを取り出して並べたリストを作成
 */

// 普通にやるとこう filter ✕ map
var filterMap = _.filter(_.range(1, 11), function(x){
    return x % 2 === 1;
}).map(function(x){
        return x;
});
console.dir( 'filterMap:' + JSON.stringify(filterMap, null, 4));

// filter ✕ reduce
var filterReduce = _.filter(_.range(1, 11), function(x){
    return x % 2 === 1;
}).reduce(function(flattened, other){
        return flattened.concat(other);
},[]);
console.dir( 'filterReduce:' + JSON.stringify(filterReduce, null, 4));

// flatMap使用
var flatMap = _.flatMap(_.range(1, 11), function (x) {
    return x % 2 === 1 ? [x] : [];
});
console.dir( 'flatMap:' + JSON.stringify(flatMap, null, 4));

// 同等
var concatMap = _.concatMap(_.range(1, 11), function (x) {
    return x % 2 === 1 ? [x] : [];
});
console.dir( 'concatMap:' + JSON.stringify(concatMap, null, 4));

lodashにはtransformがあった!

/**
 * transform
 * lodashにはtransformがあった!
 * Underscore.jsのreduceの代替
 * 結果は上記と同じ
 */
var transform_arr = _.transform(_.range(1, 11), function(result, x, i) {
    return x % 2 === 1 ? result.push(x): [];
});
console.dir( 'transform:' + JSON.stringify(transform_arr, null, 4));

find

var a = [1, 3, 6, 9];

/**
 * find
 * 集合要素に対しての操作
 * 一番目の要素を探して返す
 * => find: 6
 */
console.log( 'find: ' + _.find(a, function(num){
    return num > 5; // 6
}));

where

/**
 * where
 * 条件に合致するオブジェクトを返す
 */
console.log( 'where:' + JSON.stringify(
    _.where(PlayList, {artist : "Pharrell Williams"}), null, 4)
);

findWhere

/**
 * findWhere
 * 条件に合致する最初のオブジェクトを返す
 */
console.log( 'findWhere:' +
    JSON.stringify(
        _.findWhere(PlayList, {artist : "Pharrell Williams"}), null, 4
    )
);

filter

/**
 * filter
 * 条件に一致したものを配列で返す
 * => filter: 6,9
 */
z = _.filter(a, function(num){
    return num > 5;
});
console.log( 'filter: ' + z ); // 6, 9

contains

/**
 * contains
 * 配列内に指定値があるか => true or false
 * => contains: false
 */
console.log( 'contains: ' + _.contains(a, 10));

groupBy

/**
 * groupBy
 * 結果によるグループ化
 * groupBy: [object Object]
 */
var groupResult = _.groupBy([1, 2, 5, 8, 42, 12], function(num){
    return num % 3;
});
console.dir( 'groupBy:' + JSON.stringify(groupResult, null, 4));

countBy

/**
 * countBy
 * グループの要素カウント
 * => Object {odd: 2, even: 4}
 */
var countResult = _.countBy([1, 2, 5, 8, 42, 12], function(num){
    return num % 2 == 0 ? 'even' : 'odd';
});
// => Object {odd: 2, even: 4}
console.dir( 'countResult:' + JSON.stringify(countResult, null, 4));

sortBy

/**
 * sortBy
 * => sortBy Math sin: 5,42,12,1,2,8
 */
console.log( 'sortBy Math sin: ' + _.sortBy([1, 2, 5, 8, 42, 12], function(num){
    return Math.sin(num);
}) );

/**
 * sortBy
 * => sortBy length: i,me,and,you,dog,father
 */
console.log( 'sortBy length: ' + _.sortBy(['i', 'me', 'and', 'you', 'father', 'dog'], 'length') );

union

var c = [1, 2, 5];
var d = [2, 4, 6];

/**
 * union
 * 配列の結合(ユニーク)
 * => union: 1,2,5,4,6
 */
console.log( 'union: ' + _.union(c, d) );

intersection

/**
 * intersection
 * 配列の共通箇所を抽出
 * => intersection: 2
 */
console.log( 'intersection: ' + _.intersection(c, d) );

difference

/**
 * difference
 * 第一引数の配列を元に、比較して違いを配列で返す
 * => difference: 1,5
 */
console.log( 'difference: ' + _.difference(c, d) );

uniq

/**
 * uniq
 * => uniq: 2,5,10
 */
console.log( 'uniq: ' + _.uniq([2,5,10,2, 5]) );

template

/**
 * template
 * テンプレート機能 テンプレートを引数で置換
 * <% %> js evaluate
 * <%= %>  interpolate
 * <%- %> escape output
 */
var js_ = "<% console.log('hello from tmpl'); %>";
var html_tpl = "
  • <%- name %> - (<%- age %>)
  • "
    ; console.log(_.template( "Using 'with': <%= data.answer %>", {variable: 'data'})({answer: 'no'})); // => Using 'with': no

    Object function

    keys

    /**
     * オブジェクト操作
     * @type {{name: string, age: number, url: string}}
     */
    var user = {
        name: 'matsuoka',
        age : 27,
        url : 'http:manchan.github.io'
    };
    
    // => keys: name,age,url
    console.log( 'keys: ' + _.keys(user) );
    

    values

    // => values: matsuoka,27,http:manchan.github.io
    console.dir( 'values: ' + _.values(user) );
    

    invert

    // => Object {27: "age", matsuoka: "name", http:manchan.github.io: "url"}
    console.dir( 'invert:' + JSON.stringify(_.invert(user), null, 4) );
    

    extend 参照先もコピーされるため,参照元の値を上書きするおそれがある ※ディープクローンしたい場合は、cloneDeep

    /**
     * extend
     * _.extend(destination, *sources)
     * soucesオブジェクトのすべてのプロパティをdestinationオブジェクトにコピーする。
     * sourcesの中に同じ名前のプロパティが含まれていた場合、より後のもので上書きされる。
     */
        // => {name : 'matsuoka', age : 27}
    console.dir( 'extend:' + JSON.stringify(_.extend({name : 'matsuoka'}, {age : 27}), null, 4));
    
    var firstObj = {firstObj: 1},
        secondObj;
    secondObj = _.extend(firstObj, {secondObj: 2});
    console.log(firstObj);  // {firstObj: 1, secondObj: 2}
    console.log(secondObj);  // {firstObj: 1, secondObj: 2}
    secondObj.firstObj = 3;
    console.log(firstObj);  // {firstObj:3, secondObj:2} !上書きされている
    console.log(secondObj);  // {firstObj:3, secondObj:2}
    

    defaults

    /**
     * defaults
     * _.defaults(object, *defaults)
     * objectがdefaultsオブジェクトのプロパティを持っていない場合、
     * objectにそのdefaultsオブジェクトのプロパティを付与する。
     */
    var iceCream = {flavor : "chocolate"};
    // => {flavor : "chocolate", sprinkles : "lots"}
    console.dir( 'defaults:' + JSON.stringify(
        _.defaults(iceCream, {flavor : "vanilla", sprinkles : "lots"}),
        null,
        4));
    

    clone (shallow copy)

    /**
     * clone
     * _.clone(object)
     * objectのシャローコピーを作る。
     * ネストされたオブジェクトや配列は、参照コピーされる
     */
    var clone_obj = _.clone({name : 'matsuoka'});
    // => {name : 'matsuoka'}
    console.dir( 'clone:' + JSON.stringify(clone_obj, null, 4));
    

    has

    // => has: true
    console.log( 'has: ' + _.has(user, "name"));
    // => false
    if( !_.has(clone_obj, 'foo') ){
        // error
        // return;
    }
    

    is系(isUndefined, isEmpty, isStringなど)

    /**
     * is系は多い
     * => true or false
     */
        // => isEmpty user.name: false
    console.log( 'isEmpty user.name: ' + _.isEmpty(user.name));
    // => isNull user.age: false
    console.log( 'isNull user.age: ' + _.isNull(user.age));
    // => isString user.age: false
    console.log( 'isString user.age: ' + _.isNull(user.age));
    // => isNumber user.age: true
    console.log( 'isNumber user.age: ' + _.isNumber(user.age));
    

    shuffle

    /**
     * 関数的シャッフル
     * => shuffle1: 3,8,2,4
     */
    console.log( 'shuffle1: ' + _.shuffle([2,3,8,4]) );
    // OOP
    console.log( 'shuffle2: ' + _([2,8,10,3]).shuffle() );
    

    range

    // => range 1,10: 1,2,3,4,5,6,7,8,9
    console.log( 'range 1,10: ' + _.range(1, 10) );
    // => range 0,10: 0,1,2,3,4,5,6,7,8,9
    console.log( 'range 0,10: ' + _.range(10) );
    // => range 1,11,2: 1,3,5,7,9
    console.log( 'range 1,11,2: ' + _.range(1, 11, 2) );
    

    random

    // => random 0, 10: 2
    console.log( 'random 0, 10: ' + _.random(10) );
    

    escape

    // => escape: <script>
    console.log( 'escape: ' + _.escape('tags') );
    

    times

    // => 3 times of Hello! * 3
    _.times(3, function(){
        console.log( '3 times of ' + 'Hello!');
    });
    
    // => Ruby っぽい 3 times of Hello! * 3
    _(3).times(function(){
        console.log( ' Ruby っぽい 3 times of ' + 'Hello!');
    });
    

    Function () Functions

    bind

    /**
     * bind
     * オブジェクトを関数にバインドする。関数が呼ばれた時、
     * thisがobjectの値をとる。カリー化。
     */
    var func = function(greeting){ return greeting + ': ' + this.name };
    func = _.bind(func, {name : 'moe'}, 'hi');
    // => hi: moe
    console.log(func());
    

    bindAll

    /**
     * bindAll
     * 複数の関数をオブジェクトにバインドする。イベントハンドラーに関数をバインドするときに便利
     */
    var hoge = {
        hello:function(){console.log(' bindAll: hello '+ this.name)},
        name: 'matsuoka'
    };
    _.bindAll(hoge, 'hello');
    
    function Fuga(){}
    var fuga = new Fuga();
    fuga.name = 'you_matz';
    fuga.hello = hoge.hello;
    // => hello matsuoka
    fuga.hello();
    

    memoize

    /**
     * memoize
     * _.memoize(function, [hashFunction])
     * 関数の計算結果をキャッシュする。メモ化。
     * hasFunctionが指定された場合、結果をストアするためのハッシュキーとして利用される。
     * hasFunctionのデフォルト値は一番最初の引数。
     */
    var fibonacci = function(n) {
        return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
    };
    var fastFibonacci = _.memoize(fibonacci);
    // => 10946
    console.log( 'memoize:' + fastFibonacci(21));
    

    delay

    /**
     * delay
     * _.delay(function, wait, [*arguments])
     * setTimeoutのように、ミリ秒後に関数を呼び出す。argumentsを指定した場合、それが呼び出される関数に引き渡される。
     */
    var log = _.bind(console.log, console);
    // => 'logged later' // Appears after one second.
    _.delay(log, 1000, 'logged later');
    

    defer

    /**
     * defer
     * コールスタックが空になるまで、関数の呼び出しを遅延させる。
     * delay = 0でsetTimeoutを使う場合と同じ。
     * 複雑な計算を行ったり、UIスレッドをブロックせずにまとまったHTMLレンダリングを行いたいときに便利。
     */
    _.defer(function(){ alert('deferred'); });
    // Returns from the function before the alert runs.
    

    小技、便利な組み合わせ例

    /**
     * pluck × indexOf (findIndexで代替可能)
     * 指定した配列内のプロパティの値が、何番目に位置しているか
     *
     * pluck
     * オブジェクトの配列に使うと便利なメソッド
     * 第一引数に渡したオブジェクトの配列の中から第二引数で渡したプロパティに対応する値を配列で返します。
     * 今回の場合は「PlayList」のそれぞれのオブジェクトの値のなかから「name」の値が配列として返ってきています。
     *
     * そして、
     * indexOf
     * nameの配列から、第二引数で指定したsongが存在すればindexを返す。
     * なければ-1を返す
     */
    var PlayList = [
        {
            artist: "Pharrell Williams",
            name: "Marilyn Monroe",
            playcount: 97846,
            listeners: "32394",
            url: "http://www.last.fm/music/Pharrell+Williams/_/Marilyn+Monroe"
        },
        {
            artist: "Pharrell Williams",
            name: "Come Get It Bae",
            playcount: 113715,
            listeners: "38660",
            url: "http://www.last.fm/music/Pharrell+Williams/_/Come+Get+It+Bae"
        },
        {
            artist: "Pharrell Williams",
            name: "Happy",
            playcount: 369065,
            listeners: "98331",
            url: "http://www.last.fm/music/Pharrell+Williams/_/Happy"
        }
    ];
    
    var song = "Happy";
    // index search
    var index = _.indexOf(_.pluck(PlayList, 'name'), song);
    // => indexOf × pluck : :2
    console.dir(" indexOf × pluck :" + index);
    
    
    /**
     * filter × map
     */
    var filtered_playlist = PlayList.filter(function(list) {
        return list.playcount > 100000; // 真であれば、keepする
    }).map(function(list) {
            return { // return what new object will look like
                songname: list.name,
                playcount: list.playcount,
                songurl : list.url
            };
        });
    
    console.log( 'filter × map:' + JSON.stringify(filtered_playlist, null, 4));
    
    
    /**
     * reduce × filter
     */
    var filtered_song_middle = _.reduce(_.filter(PlayList, function(list){
        return list.playcount > 300000;
    }),function(memo, list){
        memo.push({songname: list.name})
        return memo;
    }, []);
    
    console.dir( 'reduce × filter:' + JSON.stringify(filtered_song_middle, null, 4));
    
    
    /**
     * reduceのみの場合
     */
    var reduce_playlist = PlayList.reduce(function(memo, list) {
        if (list.playcount > 300000) { // filterとして
            memo.push({ // mapとして
                songname: list.name,
                playcount: list.playcount,
                songurl : list.url
            });
        }
        return memo;
    }, []);
    
    console.dir( 'reduce:' + JSON.stringify(reduce_playlist, null, 4));
    
    /**
     * transformで少しスリムに
     */
    var transform_playlist =  _.transform(PlayList, function(result, list, i) {
        return list.playcount > 300000 ? result.push({
            songname: list.name,
            playcount: list.playcount,
            songurl : list.url
        }): [];
    });
    
    console.dir( 'transform_playlist:' + JSON.stringify(transform_playlist, null, 4));
    
    
    /**
     * max
     * 一番大きい値のオブジェクトを返す
     */
    var maxplay = _.max(PlayList, function(list){
        return list.playcount;
    });
    
    console.log( 'max:' +
        JSON.stringify(maxplay, null, 4));
    
    
    /**
     * filter × max
     * 指定した条件内にて一番大きい値のオブジェクトを返す
     */
    var filter_max = _(PlayList.filter(function(list) {
        return list.playcount < 100000; // 真であれば、keepする
    })).max(function(list){
            return list.playcount;
        });
    
    console.dir( 'filter ✕ max:' +
        JSON.stringify(filter_max, null, 4));
    
    
    /**
     * メソッドチェーン
     * value()で値取得
     * => chain value: 2,4,6
     */
    console.dir( 'chain value: ' + _.chain([1, 2, 3])
        .shuffle()
        .map(function(num){
            return num * 2;
        })
        .value());
    
    
    /**
     * オブジェクトのサイズを取得する
     * 標準jsの場合とUnderscoreの場合
     */
    console.log( ' object length:' + Object.keys(PlayList).length);
    console.log( ' object length by underscore:' +_.size(PlayList));
    
    
    /**
     * parse object to array
     * オブジェクトを配列に変換
     */
    _.each(PlayList, function(elem, key){
        PlayList[key] = _(elem).values();
    });
    
    console.log(' parse object to array: ' + PlayList);
    
    
    /**
     * 文字列をスライスして配列にコンバート
     */
    var arguments = "1234";
    var testToArray = _.toArray(arguments).slice(0,4);
    console.log(_.toArray(arguments));
    console.log(' testToArray: ' + testToArray);
    

    Underscoreにないlodash便利メソッド

    /**
     * findIndex オブジェクトのindexを取得
     * cloneDeep まるごとオブジェクトコピー(deep cloning,参照はコピーされない)
     * transform 引数に渡した配列、オブジェクトを変化させる(reduceよりもシンプルに書ける)
     */
    // 以下同じ
    // var indexByLodash = _.findIndex(PlayList, { 'name': song });
    var indexByLodash = _.findIndex(PlayList, function(list) {
        return list.name == song;
    });
    console.dir(" findIndex by lodash :" + indexByLodash);
    
    // cloneDeep _.cloneDeep(value, [customizer], [thisArg])
    var deep = _.cloneDeep(personList);
    var result2 = deep[0] === personList[0];
    console.log('cloneDeep by lodash:' + result2); // false
    
    // transform
    var tsf = _.transform([1, 2, 3, 4, 5], function(memo, idx) {
        memo[idx] = true;
    }, {});
    
    console.log('transform:' + JSON.stringify(tsf, null, 4));
    

    他の言語でのlodash(Underscore)

    Objective-C

    Underscore.m http://underscorem.org/

    Podfile
    #CocoaPodsを利用
    pod 'Underscore.m'
    
    pod install
    
    main.m
    #import 
    
        NSArray *array = @[@1, @2, @3, @4, @5, @6, @7];
        id first = Underscore.array(array).first; // @1が返る
        NSLog(@"first : %@", first);
    
        NSArray *array2 = @[@2, @4, @6, @8, @10, @12, @14];
        // クラスメソッドの場合
        id first2 = Underscore.first(array2); // @2が返る
        NSLog(@"first2 : %@", first2);
    
        NSArray *array3 = @[@1, @2, @3, @4, @5, @6, @7];
        id last = Underscore.array(array3).last; // @7が返る
    
        NSLog(@"last : %@", last);
    
        NSArray *array4 = @[@"one", @"two", @"three"];
        NSArray *capitalized = Underscore.arrayMap(array4, ^(NSString *string) {
            return string.capitalizedString;
        }); // @[@"One", @"Two", @"Three"]が返る
    
        NSLog(@"arrayMap : %@", capitalized);
    
    
        /*
         例えば「言語をキーとして挨拶文を定義したNSDictionaryがあり、
         この挨拶文に含まれる単語の先頭を大文字に変換したものを配列として取得する。
         ただし挨拶文が必ず定義されている訳ではない」といった処理
         */
        NSDictionary *dictionary = @{
                                     @"en": @"Hello world!",
                                     @"sv": @"Hej världen!",
                                     @"de": @"Hallo Welt!",
                                     @"ja": [NSNull null] // 日本語だけ挨拶文が設定されていない
                                     };
    
        NSArray *capitalizedHello = Underscore.dict(dictionary)
        .values
        .filter(Underscore.isString)
        .map(^NSString *(NSString *string) {
            return [string capitalizedString];
        })
        .unwrap;
    
        NSLog(@"capitalizedHello:%@", capitalizedHello);
    

    Swift

    同等の機能を標準(Collection API)で利用できます

    1.2からflatMapが追加されました

    MyPlayground.playground
    import UIKit
    
    
    /************************************
     filter
     クロージャに抽出条件を書き、
     そのクロージャの返り値がtrueの場合のみの配列を返す
     クロージャのパラメータを $0, $1, $2, …のように省略することができます。
     パラメータを省略するとさらに短く書けます
    ************************************/
    
    var ranges = [Int](1...10)
    var evenArr = filter(ranges, {$0 % 2 == 0})
    var evenArr2 = ranges.filter{ num in num % 2 == 0}
    println(evenArr)
    println(evenArr2)
    
    
    /************************************
     map
     クロージャが返した値を返す
    ************************************/
    
    var array = ["a10", "b10", "c11"]
    var mappedArray = map(array, { $0 + " - 1"})
    println("mapped: \(mappedArray)")
    
    var numbers = [1,2,3,4]
    let numTwice = numbers.map{
        num in num * 2
    }
    println(numTwice)
    
    
    /************************************
     reduce
     配列の中の2要素から計算して、その結果を返す。
     さらにその結果から次の要素を計算して結果を返し、それを配列分繰り返した値を返す。
    ************************************/
    
    var list1 = [1 ,2 ,3 ,4 ,5 ]
    let list1total = list1.reduce(0){ (a:Int, b:Int) -> Int in
        return a + b
    }
    println("list1 total is \(list1total)")
    
    
    var cities = ["京都", "サンクトペテルブルク", "ボゴダ", "東京", "ゴア", "バルセロナ"];
    let filter_cities =
        cities.filter{ city in (city as NSString).length >= 3}
        .reduce(0){
            (memo:Int, city:String) -> Int in
            return memo + (city as NSString).length
        }
    
    println( "filter ✕ reduce : \(filter_cities)");
    
    
    
    /************************************
     flatMap (Swift 1.2~)
     配列の要素を加工し、フラットにする(一次元配列とする)
    ************************************/
    
    var mixArr = ["2", "3", "four", "5", "six", "7"]
    // 整数と文字列が入り混じった配列から整数のみを抽出する
    
    
    // ※mapの場合
    var mapRes = mixArr.map{ $0.toInt().map{ $0 } }
    println(mapRes) // nilが混じり残念な結果に
    
    // flatMap
    var intgrouplong = mixArr.flatMap { (element: String) -> [Int] in
        if let number = element.toInt() {
            return [number]
        } else {
            return []
        }
    }
    // ワンライナー
    var intgroupshort = mixArr.flatMap { $0.toInt().map { [$0] } ?? [] }
    println(intgroupshort)
    
    var oddArray = ranges.flatMap { (x: Int) -> [Int] in ( x % 2 == 1 ) ? [x] : [] }
    var oddArray2 = ranges.filter{ num in num % 2 == 1 }
    println(oddArray)
    println(oddArray2)
    
    
    
    let strings = ["aaaa", "bbb", "cc", "d"]
    let bothCaseStrings =
    strings.flatMap {
        str in [str.uppercaseString, str.lowercaseString]
    }
    
    println(bothCaseStrings)
    

    PHP

    Underscore.php http://brianhaveri.github.io/Underscore.php/

    curl -sS https://getcomposer.org/installer | php
    
    composer.json
    {
        "require": {
            "underscore/underscore.php": "dev-master"
        }   
    }
    
    #package install
    php composer.phar install
    
    sample.php
    
    
    require 'vendor/autoload.php';
    
    /*map __::map(collection, iterator) Alias: collect
    Returns an array of values by mapping each in collection through the iterator. Arguments passed to iterator are (value, key, collection). Unlike Underscore.js, context is passed using PHP's use statement.
    */
    // array(3, 6, 9)
    print_r( __::map(array(1, 2, 3), function($num) {
        return $num * 3;
    }));
    
    // array(3, 6, 9)
    print_r( __::map(array('one'=>1, 'two'=>2, 'three'=>3), function($num, $key) {
        return $num * 3;
    }));
    
    
    /*
    pluck __::pluck(collection, propertyName)
    Extract an array of property values
    */
    $stooges = array(
        array('name'=>'moe', 'age'=>40),
        array('name'=>'larry', 'age'=>50),
        array('name'=>'curly', 'age'=>60)
    );
    
    // array('moe', 'larry', 'curly')
    print_r( __::pluck($stooges, 'name') );
    

    参考資料

    Lo-Dash本家 (http://lodash.com/docs)
    Underscore本家 (http://underscorejs.org/)
    lodash, あなたが既に使っているJavaScriptライブラリ
    http://www.infoq.com/jp/news/2015/04/lodash-utility-library
    native vs. array.js vs. underscore vs lo-dash
    http://jsperf.com/native-vs-array-js-vs-underscore/8
    npm - most depended-upon packages 
    https://www.npmjs.com/browse/depended
    Undescore.m - a functional toolbelt for Objective-C
    http://underscorem.org/
    iOSでUnderscore.jsライクに処理できるUnderscore.m〜前編〜
    http://dev.classmethod.jp/smartphone/iphone/ios-underscore-1/
    CoffeeScriptでLodash(Underscore)を書く場合
    Underscore Reference — Smooth CoffeeScript
    http://autotelicum.github.io/Smooth-CoffeeScript/literate/underscore.html