Groovy 的 Collection.inject 方法

在 Collection class 裡有一個 inject method:
public Object inject(Object initialValue, Closure closure)
這是一個很有趣的方法,看一下 API 裡的 example:

assert 1*1*2*3*4 == [1,2,3,4].inject(1) { acc, val -> acc * val }
assert 0+1+2+3+4 == [1,2,3,4].inject(0) { acc, val -> acc + val }
assert 'The quick brown fox' ==
    ['quick', 'brown', 'fox'].inject('The') { acc, val -> acc + ' ' + val }
assert 'bat' ==
    ['rat', 'bat', 'cat'].inject('zzz') { min, next -> next < min ? next : min }
def max = { a, b -> [a, b].max() }
def animals = ['bat', 'rat', 'cat']
assert 'rat' == animals.inject('aaa', max)
//Visual representation of the last example above:
//   initVal  animals[0]
//      v        v
//max('aaa',   'bat')  =>  'bat'  animals[1]
//                           v       v
//                     max('bat',  'rat')  =>  'rat'  animals[2]
//                                               v       v
//                                         max('rat',  'cat')  =>  'rat'

再看一下 mrhaki 的部落格: Groovy Goodness: Using the Inject Method

// Traditional "sum of the values in a list" sample.
// First with each() and side effect, because we have
// to declare a variable to hold the result:
def total = 0
(1..4).each { total += it }
assert 10  == total

// With the inject method we 'inject' the 
// first value of the result, and then for
// each item the result is increased and
// returned for the next iteration.
def sum = (1..4).inject(0) { result, i -> result + i }
assert 10 == sum

// We add a println statement to see what happens.
(1..4).inject(0) { result, i ->
    println "$result + $i = ${result + i}"
    result + i
}
// Output: 
// 0 + 1 = 1
// 1 + 2 = 3
// 3 + 3 = 6
// 6 + 4 = 10

class Person {
    String username
    String email
}
def persons = [
    new Person(username:'mrhaki', email: 'email@host.com'),
    new Person(username:'hubert', email: 'other@host.com')
]

// Convert list to a map where the key is the value of
// username property of Person and the value is the email
// property of Person. We inject an empty map as the starting
// point for the result.
def map = persons.inject([:]) { result, person ->
    result[person.username] = person.email
    result
}
assert [mrhaki: 'email@host.com', hubert: 'other@host.com'] == map

有沒有一些感覺了呢?inject 有點像是 recursive,不過並不一樣,它的骨子裡其實還是 for-loop。
運用 inject 簡化不少一般的寫法。

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s