Swift 丸アイコン内、テキストセンター描画
UIViewのカスタムクラスにて、overrideしたdrawRect内で、
// 丸描画
let roundRect = UIBezierPath(roundedRect: CGRectMake(1, 1, 28, 28), cornerRadius: 10)
UIColor.blueColor().setStroke()
roundRect.lineWidth = 2
roundRect.stroke()
// フォント属性
let fontAttr = [NSFontAttributeName: UIFont.systemFontOfSize(10)]
// テキスト
let str = self.initial_txt! as NSString!
// サイズ取得
let size = str.sizeWithAttributes(fontAttr)
let x_pos = (roundRect.bounds.size.width - size.width) / 2
let y_pos = (roundRect.bounds.size.height - size.height) / 2
// テキスト描画
str.drawAtPoint(CGPointMake(roundRect.bounds.origin.x + x_pos, roundRect.bounds.origin.y + y_pos),
withAttributes: fontAttr)
結果
参考
How to use NSString drawInRect to center text?
http://stackoverflow.com/questions/477509/how-to-use-nsstring-drawinrect-to-center-text
http://stackoverflow.com/questions/477509/how-to-use-nsstring-drawinrect-to-center-text
Swiftでテキスト内リンク&テキストタップ検出
実現方法
①UITextViewのプロパティの設定
textView.userInteractionEnabled = true
textView.editable = false
// tapでテキストのポジション検出可能とするため
textView.selectable = true
// 行間レイアウト用
// textView.layoutManager.delegate = self
②テキスト内リンク範囲指定
let text = "Swiftでテキスト内リンク&テキストタップ検出"
let linkText = "リンク"
let nsTex = text as NSString
let link = text.rangeOfString(linkText)
let attributedString = NSMutableAttributedString(string: text)
let start = distance(text.startIndex, link!.startIndex)
let length = distance(link!.startIndex, link!.endIndex)
// リンク位置範囲生成
range = NSMakeRange(start, length)
③textviewに指定した色、カラー、下線を指定後、代入
// テキスト全体文字色
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.whiteColor(), range: NSMakeRange(0, nsTex.length))
// リンクカラー
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.blueColor(), range: range)
// リンク下線
attributedString.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.StyleSingle.rawValue, range: range)
// 属性を代入
textView.attributedText = attributedString
④textViewにgesture追加
// gesture追加
let tap = UITapGestureRecognizer(target: self, action: "tapText:")
textView.addGestureRecognizer(tap)
⑤tap時の動作
func tapText(tap: UITapGestureRecognizer) {
// タップされた座標をもとに最寄りの文字列の位置を取得
let location = tap.locationInView(textView)
let textPosition = textView.closestPositionToPoint(location)
// テキストの先頭とタップした文字の距離をNSIntegerで取得
let selectedPosition = firstTextView.offsetFromPosition(firstTextView.beginningOfDocument, toPosition: textPosition!)
// タップした文字がリンク文字のrangeに含まれるか判定
if NSLocationInRange(selectedPosition, range) {
// リンクタップ時の処理
// let webVC = WebViewController()
// self.presentViewController(webVC, animated: true, completion: nil)
}
}
参考
UITextView でタップ可能なリンクをカスタマイズする
http://kishikawakatsumi.hatenablog.com/entry/20130605/1370370925
http://kishikawakatsumi.hatenablog.com/entry/20130605/1370370925
S3でIP制限
S3でIP制限
静的なファイルの配置先としてS3を利用している場合、アクセス制限などを設けたい場合があります。どこからでもアクセスできるとセキュリティ的にもコスト的にもあれあので。
今回は特定のIPのみを許可する方法です。
今回は特定のIPのみを許可する方法です。
実現方法
S3管理画面、特定のバケットの選択、プロパティを選択、
アクセス許可の欄より、バケットポリシーの編集をクリック
アクセス許可の欄より、バケットポリシーの編集をクリック
以下にてバケットポリシーを生成します。
http://awspolicygen.s3.amazonaws.com/policygen.html
http://awspolicygen.s3.amazonaws.com/policygen.html
説明は以下
- Select Type of Policyは「S3 Bucket Policy」を選択
- Effectは「Deny」
- Principalは「
*
」アスタリスクですべて - AWS Serviceは「Amazon S3」
- すべてのアクセスに適用させたいなら「All Actions」にチェック
- Amazon Resource はバケット以下が対象なら「
arn:aws:s3:::yourBucketName/*
」のような形で - 「Add Conditions (Optional)」をクリックでIP制限を追加
- Condition 「NotIpAddress」を選択
- Keyは「aws:SourceIp」を選択
- Valueは許可したいIPアドレスを入力
- 「Add Condition」で追加
- 「Add Statement」で正式に追加
- 複数IP追加したい場合は再度Add Conditions (Optional)で上記手順
最後に「Generate Policy」にて生成され、
S3のバケットポリシーエディターに貼り付け保存。
S3のバケットポリシーエディターに貼り付け保存。
生成されたソースの例
最後に確認
許可IPからのアクセスの場合
ファイルにアクセス可能
NGの場合、以下のXMLレスポンス
AccessDenied
Amazon Machine Learningについて
フリーランスになって三ヶ月後の振り返り
筆者背景
1987年生まれ、関西在住。
私立文系大学卒業後、Windowsパッケージソフト開発会社、WEBシステム開発会社を経て、
2015年2月よりフリーランス(個人事業主)、WEB、アプリ開発者として活動
プロフィールサイトはこちら
http://manchan.github.io/index.html
私立文系大学卒業後、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月
- iOSアプリ改修(Objective-C)
- ワードプレス改修、サーバ移行案件
3月
2月
振り返り
振り返ってみると、まぁまぁかなといったところ。
おもしろいことにフリーになったと発信すると、意外や意外、声をかえてくださったり、
仕事をくれたり、飲みに誘ってもらったり、ありがたい限りです。
4月にAppleの日本支社に行けたことがすごく刺激になった。忘れられない思い出。
個人でぼちぼちやってるレベルなので、自分が動けば動く程、良い結果が待っていると考えて、もっと切り詰めてやるべき。
もっと深くクライアントにヒアリングしたり、求めていることを具現化するべき。
継続的にお仕事いただけるところもあれば、単発で終わるところもある。
手広くなんでもやっているので、もう少しiOSアプリ中心にやっていきたい。
直近でサンプルレベルですが初めてAndroidアプリに着手しました。
iOSと違って申請後、公開まで2,3時間なのは、魅力的。
やらず嫌いはよくない。
おもしろいことにフリーになったと発信すると、意外や意外、声をかえてくださったり、
仕事をくれたり、飲みに誘ってもらったり、ありがたい限りです。
4月にAppleの日本支社に行けたことがすごく刺激になった。忘れられない思い出。
個人でぼちぼちやってるレベルなので、自分が動けば動く程、良い結果が待っていると考えて、もっと切り詰めてやるべき。
もっと深くクライアントにヒアリングしたり、求めていることを具現化するべき。
継続的にお仕事いただけるところもあれば、単発で終わるところもある。
手広くなんでもやっているので、もう少しiOSアプリ中心にやっていきたい。
直近でサンプルレベルですが初めてAndroidアプリに着手しました。
iOSと違って申請後、公開まで2,3時間なのは、魅力的。
やらず嫌いはよくない。
困ったこと
契約不履行は普通にある。
実際、報酬未払いによる集団訴訟がらみのトラブルを経験したが、自分は少額だったので、勉強代と考える。
なんでもかんでもできる事で、仕事を受ければいいというものではない。
クライアントをよく判断する。
実際、報酬未払いによる集団訴訟がらみのトラブルを経験したが、自分は少額だったので、勉強代と考える。
なんでもかんでもできる事で、仕事を受ければいいというものではない。
クライアントをよく判断する。
今後の目標
- 興味、関心があるプロジェクトに参画し、それをお金に変える(利益を求め過ぎない)
- オープンソースに貢献する(githubのstar100以上)
- 役に立ってかつ、ひとひねりある、そんなサービスを企画、開発する(アイディア or サンプルはあるがリリースまでもっていけてない、、、)
- 来年はWWDCに行く
- 仕事がらみで海外に行く
- ブログの更新頻度をあげる(最低週一)
おわりに
上記目標を今後一、二年で達成できるようにする。
まだまだこれからです。
まだまだこれからです。
lodash(Underscore.js)まとめ, Swift, Objective-C, PHPもあるよ
lodash(Underscore.js)
lodashはUnderscoreの派生ライブラリ。
Backbone.jsは、もともとunderscore.jsに依存していますし、
lodashは、JavaScriptトランスパイラのBabel,ブログプラットフォームのGhost,
プロジェクトのベースとなるツールであるYeomanなど,npmの主要なパッケージのいくつかは依存している。
Backbone.jsは、もともとunderscore.jsに依存していますし、
lodashは、JavaScriptトランスパイラのBabel,ブログプラットフォームのGhost,
プロジェクトのベースとなるツールであるYeomanなど,npmの主要なパッケージのいくつかは依存している。
AngularJsでDI(依存性注入)してlodash(underscore.js)をモジュール化して、そのまま使用してもいい。
Reactでももちろん使用可能。
Reactでももちろん使用可能。
lodash(Underscore.js)を使用する目的
配列やオブジェクトの操作をする際に便利
コードの可読性アップ
つまりコードをシンプルかつ短縮できる
for文、if文を多用せずに済む
つまり保守性が高い(変数の状態を知らなくて済む, 関数は参照透過性が保たれている)
コードの可読性アップ
つまりコードをシンプルかつ短縮できる
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
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
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:
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
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
https://www.npmjs.com/browse/depended
Undescore.m - a functional toolbelt for Objective-C
http://underscorem.org/
http://underscorem.org/
iOSでUnderscore.jsライクに処理できるUnderscore.m〜前編〜
http://dev.classmethod.jp/smartphone/iphone/ios-underscore-1/
http://dev.classmethod.jp/smartphone/iphone/ios-underscore-1/
Underscore.php
http://brianhaveri.github.io/Underscore.php/
http://brianhaveri.github.io/Underscore.php/
CoffeeScriptでLodash(Underscore)を書く場合
Underscore Reference — Smooth CoffeeScript
http://autotelicum.github.io/Smooth-CoffeeScript/literate/underscore.html
Underscore Reference — Smooth CoffeeScript
http://autotelicum.github.io/Smooth-CoffeeScript/literate/underscore.html
Wordpressサイト移行手順(さくらVPSの場合)
さくらVPSの場合
手順(ドメイン同じでサーバ移動の場合)- 移行前データベースのエクスポート, phpmyadminで、コンソール上でも
- 移行先サイトのドキュメントルートにWordpressのプログラムを一式アップロード
- 移行先データベースの新規作成(既存と同名で、照合順序はutf8-general-ci)、3でエクスポートしたSQLファイルをインポート
- 移行先URLへアクセス、問題なく表示されることを確認
- 既存ワードプレスを確認したい場合設定変更, 以下詳細
- 最終確認
移行前データベースのエクスポート, phpmyadminで、コンソール上でも
移行先サイトのドキュメントルートにWordpressのプログラムを一式アップロード
移行先データベースの新規作成(既存と同名で、照合順序はutf8-general-ci)、3でエクスポートしたSQLファイルをインポート
移行先URLへアクセス、問題なく表示されることを確認
既存ワードプレスを確認したい場合設定変更, 以下詳細
最終確認
他のサーバの場合
ドメイン同じでサーバが変更されているかの確認
手順7の後、一分ほどで新サーバで確認できます。nslookup example.com
で確認
nslookup example.com
で確認問題がある場合
移行先URLにアクセスして、TOPページは表示されるが、パーマリンクの設定を/%category%/%postname%/
のようなカスタム構造にしていて、他記事のページが404 Not Foundでかつ、リンクが文字化けする場合
Apacheのmod_rewriteモジュールの確認
# grep mod_rewrite /etc/httpd/conf/httpd.conf
LoadModule rewrite_module modules/mod_rewrite.so
# find / -name mod_rewrite.so -print
/usr/lib64/httpd/modules/mod_rewrite.so
あれば問題なし
# grep mod_rewrite /etc/httpd/conf/httpd.conf
LoadModule rewrite_module modules/mod_rewrite.so
# find / -name mod_rewrite.so -print
/usr/lib64/httpd/modules/mod_rewrite.so
Rewrite設定の確認
httpd.confの確認
"/var/www/html">
AllowOverride None
となっていればNoneをAllに変更する "/var/www/html">
AllowOverride All
これで.htaccessの設定が有効となる
"/var/www/html">
AllowOverride None
"/var/www/html">
AllowOverride All
既存のサイトも閲覧したい場合
以下SQLを既存サイト(http://ホスト名/phpmyadmin/
)で実行#wordpressのドメインの変更
UPDATE wp_options SET option_value =
REPLACE (option_value, 'old_url', 'new_url')
WHERE option_name = 'home' OR option_name = 'siteurl';
#記事のリンクの変更
UPDATE wp_posts SET guid = REPLACE (guid, 'old_url', 'new_url');
#記事内のURLの変更
UPDATE wp_posts SET post_content =
REPLACE (post_content, 'old_url', 'new_url');
#画像パスの変更
UPDATE wp_posts SET post_content =
REPLACE (post_content, 'src="old_url', 'src="new_url');
UPDATE wp_posts SET guid =
REPLACE (guid, 'old_url', 'new_url')
WHERE post_type = 'attachment';
#各投稿メタ情報を変更
UPDATE wp_postmeta SET meta_value =
REPLACE (meta_value, 'old_url','new_url');
それぞれold_url, new_urlは書き換えてください。
http://ホスト名/phpmyadmin/
)で実行#wordpressのドメインの変更
UPDATE wp_options SET option_value =
REPLACE (option_value, 'old_url', 'new_url')
WHERE option_name = 'home' OR option_name = 'siteurl';
#記事のリンクの変更
UPDATE wp_posts SET guid = REPLACE (guid, 'old_url', 'new_url');
#記事内のURLの変更
UPDATE wp_posts SET post_content =
REPLACE (post_content, 'old_url', 'new_url');
#画像パスの変更
UPDATE wp_posts SET post_content =
REPLACE (post_content, 'src="old_url', 'src="new_url');
UPDATE wp_posts SET guid =
REPLACE (guid, 'old_url', 'new_url')
WHERE post_type = 'attachment';
#各投稿メタ情報を変更
UPDATE wp_postmeta SET meta_value =
REPLACE (meta_value, 'old_url','new_url');