2016年3月6日 星期日

[SQL] 於多筆重複資料中取得該重複群組中最新一筆資料

最近要做一件特別的SQL查詢,有個一個Table1,如下所示

Table1 :
group_iduser_id
111
1111
111111
222
2222
333
3333

我們想要選出各group_id最大的user_id,也就是說我們希望的結果要像是如下這樣:
group_iduser_id
111111
2222
3333

先來看一下ROW_NUMBER()語句的,如下所示,詳細資料可參考這裡

ROW_NUMBER ( ) OVER ( [ PARTITION BY value_expression , ... [ n ] ] order_by_clause )

它可對傳回結果集的各分割區進行編列序號,各分割區序號從 1 開始,利用這個我們就可完成我們的需求。

思路是這樣的:先把Table1以group_id進行分組(partition),各組進行以user_id的DESC排序並標上編號(例如group_id=1的三組資料編號1,2,3、group_id=2的二組資料編號1,2),接著取出各組最上面那行資料,也就是各組編號為1的那行即可。

這時我們就可以使用rownumber, over, partition語法指令來幫助我們達成目標。

我們先用以下語法對Table1以group_id進行分組,並對各組以user_id做DESC排序,並為給組的排序標上編號,Sort
SELECT *, ROW_NUMBER() OVER (PARTITION BY group_id Order By user_id DESC) As Sort 
FROM Table1

這樣就會得到以下結果:
group_iduser_idSort
1111111
11112
1113
22221
2222
33331
3331

接著在跟Table1結合並選出Sort=1但不會select Sort欄位就行了,可以使用如下語句:
SELECT group_id, user_id FROM (
         SELECT *, ROW_NUMBER() OVER (PARTITION BY group_id Order By user_id DESC) As Sort
         FROM Table1
) SortTable
WHERE SortTable.Sort = 1
這樣就大功告成了。

參考資料:
  1. [SQL] 於多筆重複資料中取得該重複群組中最新一筆資料

Parse ISO 8601 duration 的方法

ISO 8601 Duration是ISO 8601的Duration標準,是用來表示持續時間的字串,例如
"P3Y6M4DT12H30M5S" 就表示 "3年6個月4天12小時又30分5秒",
像Youtube A{I回傳的影片長度就是用這個標準的字串來表示,
在Java中,除了自己想辨法解析外,也可以使用以下的方法:
  1. 在Java 1.8之後,只要使用java.time.Duration這個Class就可以輕易地解析由ISO 8601 Duration表示的持續時間,範例程式如下:
  2. import java.time.Duration;
    import java.text.NumberFormat;
    import java.text.DecimalFormat;
    
    public class HelloWorld
    {
      public static void main(String[] args)
      {
        Duration duration = Duration.parse("PT58S");
        NumberFormat formatter = new DecimalFormat("00");
    
        String hours = formatter.format(duration.toHours());
        String minutes = formatter.format(duration.toMinutes());
        String seconds = formatter.format(duration.getSeconds());
    
        System.out.print(hours + ":" + minutes + ":" + seconds); //00:00:58
      }
    }

  3. 而在Java 1.8之前,因為沒有java.time.Duration,所以可以借助第三方的Class,在這邊介紹一個很好用的第三方JAR,joda-time,到官網下載引用後,就可以使用它的Period, PeriodFormatter, ISOPeriodFormater等Class來幫助我們解析ISO 8601 Duration,範例程式如下:
  4. import java.text.DecimalFormat;
    import java.text.NumberFormat;
    import org.joda.time.Period;
    import org.joda.time.format.ISOPeriodFormat;
    import org.joda.time.format.PeriodFormatter;
    
    public class DurationTest {
    
     public DurationTest() {
      // TODO Auto-generated constructor stub
     }
    
     public static void main(String[] args) {
      PeriodFormatter periodformatter = ISOPeriodFormat.standard();
      Period p = periodformatter.parsePeriod("PT58S");
      NumberFormat numberformatter = new DecimalFormat("00");  
    
      String hours = numberformatter.format(p.getHours());
      String minutes = numberformatter.format(p.getMinutes());
      String seconds = numberformatter.format(p.getSeconds());
    
      System.out.println(hours + ":" + minutes + ":" + seconds); //00:00:58
     }
    }
參考資料:
  1. How to convert Youtube API V3 duration in Java

2016年2月6日 星期六

Javascript的原型

Javascript與我們熟知的Java及C++不同,它沒有Java及C++那種繼承(extends)與實作介面(implement interface)的概念,而是有自己獨特的「原型鏈」特性,對沒接觸過的人可能會有點難以理解,所以在這篇文我特地為其做了筆記。

在Javascript中,幾乎任何東西(除了Boolean, Number那些)都是類別(Obejct),而在類別中有三個有趣的屬性:constructor、prototype、_proto_。

當我們在使用「new」這個關鍵字時,就會用到以上三個屬性,例如:

var car1 = new Car();

它的行為就是:

  1. 為 car1 建立一個實例(instance)。
  2. 將 car1._proto_ 指向 Car.prototype 。
  3. 將上下文設定成 car1 (即 this 代表 car1) 來執行 Car.prototype.constructor()。
為了容易理解,可以用下圖來表示:




對應的程式碼可能如下:
var Transportation = function(){......};
var Car = function(){......};
Car.prototype = new Transportation();
var car1= new Car();

當要使用的變數在bus中找不到時,它會去car1._proto_中去尋找,如果還是找不到,就會到
bus._proto_._proto_中去尋找,以此類推,直到找盡頭(XXX._proto_ = null)為止。

利用這樣的特性,就可以模擬(不完全相同)Java中的繼承關係,例如上例就有如 Car 繼承 Transportation、 bus 繼承 Car這樣的關係。

如果想要再創一個Bus去繼承Car的話,就可以像這樣:
var Bus = function(){......};
Bus.prototype = new Car();



參考資料:

  1. JAVASCRIPT原型
  2. 基於原型的JavaScript繼承
  3. javascript 原型模式的工作原理 到 對象模式的探尋(上)
  4. 實體關係的確定
  5. JavaScript's Pseudo Classical Inheritance diagram





2016年1月3日 星期日

freemarker - Java :遍歷Hashmap

template中遍歷Java傳來的HashMap參數

因為不是Freemarker自己類型的Map,所以不能直接用下列方式取值遍歷:

<list map?keys as key>
${key} : ${map[key]}
</list>


原因是Freemarker判斷map可以用點(".")的方式得到的都為其擁有的名值對,所以會把map的所有Java api method名稱都列出來,例如put, keySet, entrySet ...

以下才是正確的遍歷方式:

Java碼:
HashMap testMap = new HashMap();
testMap.put("t1", "tt1");
testMap.put("t2", "t2");
this.context.put("testMap", testMap);

Template碼:
<#list testMap.entrySet() as entry>
${entry.getKey()} = {entry.getValue()}
</#list>


<#list testMap.keySet() as key>
${key} = ${testMap.get(key)}
</#list>

2015年12月13日 星期日

jQuery之Deffered的應用

jQuery有Deferred及Promise物件,可以幫助我們處理異步函式的callback順序關係。
在這邊舉兩個例子來說明其可能的兩個應用:

一、用Deferred實現異步函式的callback順序關係
Deferred可以實現異步函式的callback順序關係,在下例中,我們有三個函式,f1()、f2()、f3(),其中都有setTimeout模擬一秒的異步延遲,並且希望能f1做完才執行f2、f2做完才執行f3。如果我們沒有處理好callback的順序關係的話,就會發生f1的setTimeout還沒執行完就執行f2的情況。

在此例中,我們利用Deferred.then()來確保f1,f2,f3的執行順序關係,在f1,f2,f3中,會個自建立一個Deferred物件並傳回。
而setTimeout結束後,會呼叫Deferred.resolve()來告知函式執行完成,並可以選擇送進一個參數(param)來給之後的callback函式使用,接著會執行then()裡面的函式。

以上說明是簡單的行為了解。其實真正的實作比較複雜,可以參考jQuery之Deferred的原理,在這邊稍微講一下大概的背後過程:

  1. f1()回傳了第一個Deferred物件。
  2. f1('f1').then(f2)建立了第二個Deferred物件,並為第一個Deferred物件設定done(ff2),ff2是callback函式,ff2的內容是執行f2(),並取得f2()回傳的第三個Deferred物件,接著設定第三個物件的done(fff2),其中fff2=第二個Deferred物件.resolve。
  3. 所以當呼叫then(f2)時,會回傳一個Deferred物件(即第二個Deferred物件。
  4. 當f1()裡呼叫defer.resolve()時,會執行f2()。
  5. 當f2()裡呼叫defer.resolve()時,會引發第三個Deferred物件的done(),接著就會引發第二個Deferred物件的resolve()。
  6. 以此類推,第二個Deferred物件被resolve()後就會去執行f3()。


HTML:
<div id='result'>
  Result:
</div>
Javascript:
function f1(param) {
  var defer = $.Deferred();
  setTimeout(function() {
    $('#result').html($('#result').html() + '<br/>f1 got parameter : ' + param);
    //將'f2'當參數傳給don()或then()之後的function
    defer.resolve('f2');
  }, 1000);
  return defer;
}

function f2(param) {
  var defer = $.Deferred();
  setTimeout(function() {
    $('#result').html($('#result').html() + '<br/>f2 got parameter : ' + param);
    //將'f3'當參數傳給don()或then()之後的function
    defer.resolve('f3');
  }, 1000);
  return defer;
}

function f3(param) {
  var defer = $.Deferred();
  setTimeout(function() {
    $('#result').html($('#result').html() + '<br/>f3 got parameter : ' + param);
    //defer.resolve(param + ' f3');
  }, 1000);
  return defer;
}
//Deferred參數的傳遞
f1('f1').then(f2).then(f3);
二、合併多個異步函式的callback處理,即個別異步函式都執行完後才執行callback

在此例中我們用到了$.when(),它可以被傳入多個Deferred或Promise物件,並在所有的Deferred或Promise物件都被resolve()或reject()後才執行callback。

在這裡我們有三個動畫,都為一串文字由右往左移動,每個動畫的時間都不一樣,第一個是用CSS的動畫,其他兩個是用jQuery的animate()做的動畫。

對於每個動畫,我們都建立一個新的Deferred物件給它,並在動畫執行完後將它得到的Deferred給resolve()。而每個Deferred物件我們都放進一個叫des的Array中。

最後我們把Array裡面的三個Deferred物件丟給$.when()裡當參數,利用then()或done()來設定三個Deferred物件都resolve()後才要執行的callback。

P.S.

  1. 因為$.when()不能接受Array型式的參數,所以這裡利用了apply($,des)來將des中的內容傳給when(),其中第一個參數為要代替when()中的this的參考,第二個參數是一個Array,其內容為要傳給when()的參數。
  2. resolveDeferred()接受一個Deferred物件,並傳回一個function,在傳回的function中其this指向呼叫resolveDeferred的物件;而deferred指向function被建立的域(即resolveDeferred())中的deferred。(可參見閉包(Closure))


HTML:
<div class='animSection'>
  <div id='anim1'>Watch me move1</div>
  <div id='anim2'>Watch me move2</div>
  <div id='anim3'>Watch me move3</div>
</div>
<br/>
<div id='text' class="text">Detect Result:</div>
CSS:
.animSection div{
  padding-left: 100%;
}

.animClass {
  animation-name: myAnim;
  animation-duration: 6s;
  animation-fill-mode: forwards;
}

@keyframes myAnim {
  0% {
    padding-left: 100%;
  }
  100% {
    padding-left: 0%;
  }
}
Javascript:
var des = []; //用來放Deferred或Promise物件的array
var deferred;

deferred = $.Deferred();
des.push(deferred.promise()); 
//也可以寫成des.push(deferred),deferred.promise()回傳的為Promise物件,為不可修改狀態只供查詢狀態的物件,即沒有resolve()等方法
//
$('#anim1').addClass('animClass').one('animationend', resolveDeferred(deferred));

deferred = $.Deferred();
des.push(deferred.promise());
$('#anim2').animate({
  paddingLeft: "-=100%"
}, 9000, resolveDeferred(deferred));

deferred = $.Deferred();
des.push(deferred.promise());
$('#anim3').animate({
  paddingLeft: "-=100%"
}, 5000, resolveDeferred(deferred));
//因為$.when()只能接收一個一個用逗號分開的參數,所以利用apply來將array以個別
//參數的方式傳給when(),when()可接受Deferred及Promise物件
$.when.apply($, des).done(function() {
  $('#text').html($('#text').html() + '<br/>All finished!');
});
//也可以寫成以下,then可以給兩個參數,第一個為done()的callback函式,
//第二個為fail()的callback函式
//$.when.apply($, des).then(function(){
//	$('#text').html($('#text').html() + '<br/>All finished!');
//});

function resolveDeferred(deferred) {
  return function() {
    $('#text').html($('#text').html() + '<br/>#' + this.id + ' finished!');
    deferred.resolve();
  }
}

2015年12月12日 星期六

jQuery之Deferred的原理(參看源碼)

了解了jQuery的Callbacks以後,我們可以藉由觀看jQuery的源始碼(jquery-1.11.3.js)來較清楚地了解Deferred到底做了什麼事及怎麼實現的,以更好的運用Deferred及其相關API。

首先,我們先把Deferred的jQuery源碼貼出來:

 Deferred: function( func ) {
  var tuples = [
    // action, add listener, listener list, final state
    [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
    [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
    [ "notify", "progress", jQuery.Callbacks("memory") ]
   ],
   state = "pending",
   promise = {
    state: function() {
     return state;
    },
    always: function() {
     deferred.done( arguments ).fail( arguments );
     return this;
    },
    then: function( /* fnDone, fnFail, fnProgress */ ) {
     var fns = arguments;
     return jQuery.Deferred(function( newDefer ) {
      jQuery.each( tuples, function( i, tuple ) {
       var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
       // deferred[ done | fail | progress ] for forwarding actions to newDefer
       deferred[ tuple[1] ](function() {
        var returned = fn && fn.apply( this, arguments );
        if ( returned && jQuery.isFunction( returned.promise ) ) {
         returned.promise()
          .done( newDefer.resolve )
          .fail( newDefer.reject )
          .progress( newDefer.notify );
        } else {
         newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
        }
       });
      });
      fns = null;
     }).promise();
    },
    // Get a promise for this deferred
    // If obj is provided, the promise aspect is added to the object
    promise: function( obj ) {
     return obj != null ? jQuery.extend( obj, promise ) : promise;
    }
   },
   deferred = {};

  // Keep pipe for back-compat
  promise.pipe = promise.then;

  // Add list-specific methods
  jQuery.each( tuples, function( i, tuple ) {
   var list = tuple[ 2 ],
    stateString = tuple[ 3 ];

   // promise[ done | fail | progress ] = list.add
   promise[ tuple[1] ] = list.add;

   // Handle state
   if ( stateString ) {
    list.add(function() {
     // state = [ resolved | rejected ]
     state = stateString;

    // [ reject_list | resolve_list ].disable; progress_list.lock
    }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
   }

   // deferred[ resolve | reject | notify ]
   deferred[ tuple[0] ] = function() {
    deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
    return this;
   };
   deferred[ tuple[0] + "With" ] = list.fireWith;
  });

  // Make the deferred a promise
  promise.promise( deferred );

  // Call given func if any
  if ( func ) {
   func.call( deferred, deferred );
  }

  // All done!
  return deferred;
 }

再來一步步解析:

jQuery的Callbacks

jQuery有一個對控制異步函式時很好用的類別Deferred,還有它的簡少API版本,Promise(無法更改state)。

我們可以藉由觀看jQuery的源始碼(jquery-1.11.3.js)來較清楚地了解Deferred到底做了什麼事及怎麼實現的,以更好的運用Deferred及其相關API。

在了解Deferred之前,我們可以先來了解Callbacks這個類別。在這邊用形象的方式來了解Callbacks的運作方式。

Callbacks可以被當成是一個佇列式的列隊(先進先出,FIFO),我們可以把想要做的function傳進列隊中(add()),並在想要的時候觸發列隊中的function執行(fire() or fireWith()),在這邊是一次執行所有的function,雖然有照順序開始執行,但並無法保證一個function執行完才執行下一個(例如要花時間的異步函式)。

而Deferred則是利用Callbacks的性質來實作的,基本上就是預先將異步函式做完時要執行的回調函式先放到列隊中,並執行異步函式,等異步函式執行完後手動決定要觸發列隊裡函式並執行的時機。

jQuery的Callbacks有以下常用的API function
  1. $.Callbacks([flags]) : 創建並返回一個Callbacks類別。
    • flags : 可選,可以接受多個,用空格的String來表示,例如有以下幾種:
      •  once:
        • 列隊只能被觸發一次(fire)。
      •  memory:
        • 列隊觸發後,一有函式放進(add)列隊會被馬上執行。
      •  unique:
        • 列隊中的同樣函式(指的是其參考一樣的 reference)再一次觸發時只會被執行一次。
  2. Callbacks.add(callbacks) : 將callbacks函式放進Callbacks列隊中。
  3. Callbacks.fire([arguments]) : 觸發列隊執行其中的函式。
    • arguments : 可選,可傳入參數給列隊中各函式當input value。
  4. Callbacks.fireWith([context] [, arguments]) : 跟fire()相似,也是觸發列隊執行其中的函式,不過可以指定上下文(context),即函式中的this指誰。
  5. Callbacks.disable() : 將callback列隊disable掉,即之後不能在做fire()之類的呼叫(呼叫了也不會有行為)
範例 :