Skip to content

Instantly share code, notes, and snippets.

@manchan
Last active August 29, 2015 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save manchan/cc72225b813bea47d1d0 to your computer and use it in GitHub Desktop.
Save manchan/cc72225b813bea47d1d0 to your computer and use it in GitHub Desktop.
Lodash(Underscore) practice of multiple language(Objective-C,Javascript, PHP, Swift ver 1.2(Standard Function))
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <Underscore.h>
int main(int argc, char * argv[]) {
@autoreleasepool {
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);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
// latest 3.7.0
var _ = require('lodash');
/**
* lodash(Underscore.js) の便利な関数ライブラリたち
* 「_」というオブジェクトを使用することにより利用可能
* 配列、オブジェクト操作を便利にするもの
*
* よく使うのは以下
* each
* find
* filter
* where
* findWhere
* contains
* pluck
* max
* min
* sortBy
*/
/**
* Collection Functions
*/
/**
* ループ処理 collection each
* => each: 4
* => each: 10
* => each: 16
*/
_.each([2, 5, 8], function(num){
console.log( 'each: ' + num * 2 );
});
var sample = {
hello: "worls",
world: "hello"
};
/**
* 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 );
/**
* 文字列配列の内、三文字以上のものを選択し、
* 文字数の合計を集計する
* => 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 メソッドチェーンで使用
* 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を使用したい場合
*/
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;
});
// filter ✕ reduce
var filterReduce = _.filter(_.range(1, 11), function(x){
return x % 2 === 1;
}).reduce(function(flattened, other){
return flattened.concat(other);
},[]);
// flatMap使用
var flatMap = _.flatMap(_.range(1, 11), function (x) {
return x % 2 === 1 ? [x] : [];
});
// 同等
var concatMap = _.concatMap(_.range(1, 11), function (x) {
return x % 2 === 1 ? [x] : [];
});
/**
* 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( 'filterMap:' + JSON.stringify(filterMap, null, 4));
console.dir( 'filterReduce:' + JSON.stringify(filterReduce, null, 4));
console.dir( 'flatMap:' + JSON.stringify(flatMap, null, 4));
console.dir( 'concatMap:' + JSON.stringify(concatMap, null, 4));
console.dir( 'transform:' + JSON.stringify(transform_arr, null, 4));
var a = [1, 3, 6, 9];
/**
* find
* 集合要素に対しての操作
* 一番目の要素を探して返す
* => find: 6
*/
console.log( 'find: ' + _.find(a, function(num){
return num > 5; // 6
}));
/**
* filter
* 条件に一致したものを配列で返す
* => filter: 6,9
*/
z = _.filter(a, function(num){
return num > 5;
});
console.log( 'filter: ' + z ); // 6, 9
/**
* contains
* 配列内に指定値があるか => true or false
* => contains: false
*/
console.log( 'contains: ' + _.contains(a, 10));
/**
* 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
* グループの要素カウント
* => 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 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') );
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: 2
*/
console.log( 'intersection: ' + _.intersection(c, d) );
/**
* difference
* 第一引数の配列を元に、比較して違いを配列で返す
* => difference: 1,5
*/
console.log( 'difference: ' + _.difference(c, d) );
/**
* uniq
* => uniq: 2,5,10
*/
console.log( 'uniq: ' + _.uniq([2,5,10,2, 5]) );
/**
* template
* テンプレート機能 テンプレートを引数で置換
* <% %> js evaluate
* <%= %> interpolate
* <%- %> escape output
*/
var js_ = "<% console.log('hello from tmpl'); %>";
var html_tpl = "<li><%- name %> - (<%- age %>)</li>";
console.log(_.template(
"Using 'with': <%= data.answer %>",
{variable: 'data'})({answer: 'no'})); // => Using 'with': no
/**
* Object function
*/
/**
* オブジェクト操作
* @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: matsuoka,27,http:manchan.github.io
console.dir( 'values: ' + _.values(user) );
// => Object {27: "age", matsuoka: "name", http:manchan.github.io: "url"}
console.dir( 'invert:' + JSON.stringify(_.invert(user), null, 4) );
/**
* 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}
/*
* 参照先もコピーされるため
* ※ディープクローンしたい場合は、cloneDeep
*/
/**
* 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
* _.clone(object)
* objectのシャローコピーを作る。
* ネストされたオブジェクトや配列は、参照コピーされる
*/
var clone_obj = _.clone({name : 'matsuoka'});
// => {name : 'matsuoka'}
console.dir( 'clone:' + JSON.stringify(clone_obj, null, 4));
// => has: true
console.log( 'has: ' + _.has(user, "name"));
// => false
if( !_.has(clone_obj, 'foo') ){
// error
// return;
}
/**
* 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));
/**
* 関数的シャッフル
* => shuffle1: 3,8,2,4
*/
console.log( 'shuffle1: ' + _.shuffle([2,3,8,4]) );
// OOP
console.log( 'shuffle2: ' + _([2,8,10,3]).shuffle() );
// => 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 0, 10: 2
console.log( 'random 0, 10: ' + _.random(10) );
// => escape: &lt;script&gt;
console.log( 'escape: ' + _.escape('<script>') );
// => 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 (uh, ahem) Functions
*/
/**
* bind
* オブジェクトを関数にバインドする。関数が呼ばれた時、
* thisがobjectの値をとる。カリー化。
*/
var func = function(greeting){ return greeting + ': ' + this.name };
func = _.bind(func, {name : 'moe'}, 'hi');
// => hi: moe
console.log(func());
/**
* 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(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(function, wait, [*arguments])
* setTimeoutのように、ミリ秒後に関数を呼び出す。argumentsを指定した場合、それが呼び出される関数に引き渡される。
*/
var log = _.bind(console.log, console);
// => 'logged later' // Appears after one second.
_.delay(log, 1000, 'logged later');
/**
* defer
* コールスタックが空になるまで、関数の呼び出しを遅延させる。
* delay = 0でsetTimeoutを使う場合と同じ。
* 複雑な計算を行ったり、UIスレッドをブロックせずにまとまったHTMLレンダリングを行いたいときに便利。
*/
_.defer(function(){ alert('deferred'); });
// Returns from the function before the alert runs.
/**
* 小技、便利な組み合わせ例
*/
/**
* pluck × indexOf
* 指定した配列内のプロパティの値が、何番目に位置しているか
*
* 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);
/**
* 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);
var personList = [
{ 'name': 'Haru39', 'age': 26 },
{ 'name': 'yutapon', 'age': 18 }
];
// clone
var clone = _.clone(personList);
var result1 = clone[0] === personList[0];
console.log(result1); // true
// 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));
/**
* 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));
/**
* where
* 条件に合致するオブジェクトを返す
*/
console.log( 'where:' + JSON.stringify(
_.where(PlayList, {artist : "Pharrell Williams"}), null, 4)
);
/**
* findWhere
* 条件に合致する最初のオブジェクトを返す
*/
console.log( 'findWhere:' +
JSON.stringify(
_.findWhere(PlayList, {artist : "Pharrell Williams"}), 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);
console.log(' testToArray: ' + testToArray);
<?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') );
import UIKit
/************************************
filter
クロージャに抽出条件を書き、
そのクロージャの返り値がtrueの場合のみの配列を返す
************************************/
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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment