2016年9月5日 星期一

Google Sheets: Array Lesson 3: 陣列-維持-多值 (Array-Keep-Multiple value)

瞭解Google試算表(Sheets)的陣列交集規則之後,心中可能會有個疑問,難道陣列一定要進行交集,一定要變換成單值嗎?不能讓陣列保持多值的形式嗎?

的確可以,而且這種保持多值的能力,還能被進一步強化,構成所謂的「陣列公式(array formula)」,提供類似傳統電腦程式語言中的迴圈功能。

1. 單值旗標:Keep-single

Google試算表提供了許多的函式,每個函式通常都會接受一些參數,作為該函式運算時的依據。如果觀察這些函式的參數,會隱隱約約感覺到,每個參數似乎有個(看不見的)旗標,控制該參數在遇到陣列時應該如何反應。

想像這個旗標,也許稱它為「單值旗標」。凡是被標上這個旗標的參數,都會期待傳給它的值是一個單值(non-array; single-value; scalar)。如果傳給它一個陣列(array; multiple-value),那麼在把值傳入函式之前,系統會先想辦法從這個陣列中找出一個單值(也就是,交集規則),然後才傳入該函式中。

相反的,對於那些沒有被標上單值旗標的參數,即使接收到陣列,系統也不會進行什麼特別的處理,就只是原封不動的把整個陣列傳入函式中。

2. 沒有單值旗標:Keep-array

先看看最單純的情況:沒有單值旗標。有些函式的參數,不論傳入的是單值(純量;scalar)、還是多值(陣列;array),它都會照單全收(Keep-array),不會試著把多值轉成單值(也就是不會使用交集規則)。函式FILTER()SUM()就是這樣的例子:



不管傳入的陣列是「儲存格範圍」、還是「函式傳回陣列」,FILTER()與SUM()對它們的處理方式都相同,就是全部拿來使用。這種參數,可以想像它們就是沒有被標示單值旗標。

3. 兩種單值旗標

由於陣列的來源有兩種(儲存格範圍、函式傳回陣列),這個單值旗標似乎也有兩個,控制該參數在遇到這兩種陣列時,是否要取單值。

註:上例中,欄位F、G、H這三欄,是把同列欄位B中的公式,加上ARRAYFORMULA()之後,例如,把ADD($A$1:$C$1,500)變成ARRAYFORMULA(ADD($A$1:$C$1,500)),所得到的結果。

3.1 單值旗標:針對「儲存格範圍」取單值:Keep-single for cell range

具有這個旗標的參數,在傳入「儲存格範圍」時,就會試著取得該範圍中的某個單值。取值的規則,就是交集規則(把該範圍與公式所在儲存格的欄或列位址進行交集)。

ADD()的所有參數。
IF()的所有參數。
CHOOSE()的所有參數。
INDEX()的第二個參數。
VLOOKUP()的第一、第三個參數。

例外
INDEX()的第一個參數:Keep-array。
VLOOKUP()的第二個參數:Keep-array。

3.2 單值旗標:針對「函式傳回陣列」取單值:Keep-single for returned array

具有這個旗標的參數,在傳入「儲存格範圍」時,就會試著取得該範圍中的某個單值。取值的規則,是只取陣列最左上角的元素(而不是與公式所在儲存格的欄或列位址進行交集)。

ADD()的所有參數。
IF()的第一個參數。
CHOOSE()的第一個參數。
INDEX()的第二個參數。
VLOOKUP()的第一、第三個參數。

例外
IF()的第二個參數:Keep-array。
CHOOSE()的第二個參數:Keep-array。
INDEX()的第一個參數:Keep-array。
VLOOKUP()的第二個參數:Keep-array。

3.3 有規則嗎?

感覺上,規則似乎有點混亂(說不定根本就沒有規則)。為了容易記憶起見,也許可以這樣歸納:
  • 一般的函式
    • FILTER()、SUM()、ADD()
    • 單值的參數,傾向於同時具有兩種單值旗標。
    • 多值的參數,傾向於沒有任何單值旗標。
  • 選擇型函式
    • IF()、CHOOSE()、INDEX()、VLOOKUP()、HLOOKUP()
    • 特徵:根據索引值從多個值中選擇出一個值的函式。
    • 擔任索引值的參數,似乎都傾向於取用單值(Keep-single)。
      • IF(1st,...)、CHOOSE(1st,...)、INDEX(...,2nd,3rd)、VLOOKUP(1st,...,3rd,...)、HLOOKUP(1st,...,3rd,...)
    • 而要被從中選擇的參數,則似乎傾向於維持多值(Keep-array)。
      • IF(...,2nd,3rd)、CHOOSE(...,2nd,3rd,4th)、INDEX(1st,...)、VLOOKUP(...,2nd,...)、HLOOKUP(...,2nd,...)
    • 但這不是絕對的規則,個別函式,仍可能有不同的特性。
      • IF(...,2nd,3rd)、CHOOSE(...,2nd,3rd,4th)雖然是被選擇的標的,但是具有Keep-single for cell range旗標,卻不具有Keep-single for returned array旗標。

4. 抑制單值旗標:強制維持多值(Forcibly Keep-array)

為什麼要「想像」可能有所謂的單值旗標(Keep-single)呢?這是因為如果沒有這種維持單值的概念,似乎就不容易理解ARRAYFORMULA()這個函式的效果。

簡單的說,函式ARRAYFORMULA()會抑制單值旗標,強迫它涵蓋下的每個參數,在接收到陣列時,都會盡可能維持多值(Keep-array)。
  • 這在某些函式的身上,效果十分明顯而純粹。
    • 例如:ADD()。
  • 但是在另外一些函式身上,好像沒有效果。
    • Cell Range仍然維持Keep-single for cell range的效果(交集規則)。
      • 例如:CHOOSE(1st,...)。在上例中,由於公式ARRAYFORMULA( CHOOSE( $A$1:$C$1, 4, 5, 6 ) )所在儲存格$F$9的欄位址$F:$F,已經無法與參數範圍$A$1:$C$1產生交集,所以得到#NUM!的錯誤值。
      • 例如:INDEX(...,2nd,3rd)。在上例中,由於公式ARRAYFORMULA( INDEX( {1,2,3;4,5,6}, 2, $A$1:$B$1 ) )所在儲存格$F$12的欄位址$F:$F,已經無法與參數範圍$A$1:$B$1產生交集,所以似乎被使用0取代,得到等同於ARRAYFORMULA( INDEX( {1,2,3;4,5,6}, 2, 0 ) )的效果,傳回一整列。
    • Function Returned Array也似乎仍維持Keep-single for returned array的效果。
      • 例如:INDEX(...,2nd,3rd)。在上例中,儲存格$F$25的公式ARRAYFORMULA( INDEX( {1,2,3;4,5,6}, 2, { $A$1:$B$1 } ) ),仍然固定取用傳入陣列的最左上角元素。

4.1 ARRAYFORMULA()的涵蓋範圍

ARRAYFORMULA()的影響範圍會遞迴,會從它出現的位置開始,往它所包含的運算式,逐層遞迴,直到最深最內層的函式為止。