Abstract
Pythonでよく使うenumerate
をPowershellで実装しました。
Pythonのenumerateは下記のようなものです。
for i, elem in enumerate([1, 2, 3, 4, 5]):
# `i`にindexが、`elem`に各要素が入っている
print(i + elem)
成果物
function enumerate([parameter(mandatory)][scriptblock]$callback, [Parameter(ValueFromPipeline)][array]$iterable, [int]$start = 0) {
<#
.SYNOPSIS
pythonic `enumerate` function.
.DESCRIPTION
This function iterates a given array-like object with the index number and its corresponding element.
.PARAMETER callback
A callback function or process which can take two arguments `($i, $element)`.
In this callback function, $i and $_ can be used as a inedx and the i-th element, respectively.
.PARAMETER iterable
An array object. If no object is given, this function will try to obtain the value from `$input`, which is generated automatically in the context of `filter`.
.PARAMETER start
An index number that determines what number the enumerate loop starts with. defaults to `0`.
.EXAMPLE
C:\Users\takeMe1010> $temp = 1..5
# the most simplest case like the `filter`
C:\Users\takeMe1010> $temp | enumerate {Write-Output ($_ + $i)}
1
3
5
7
9
.EXAMPLE
# the case like the `function`
C:\Users\takeMe1010> enumerate {Write-Output ($_ + $i)} -iterable $temp
1
3
5
7
9
.EXAMPLE
# with the anonymous function
C:\Users\takeMe1010> $temp | enumerate {
>> param (
>> [Int32]$i,
>> [Int32]$value
>> )
>> Write-Output ($value + $i)
>> }
1
3
5
7
9
.EXAMPLE
# the index number starts with 2
C:\Users\takeMe1010> enumerate {$i} -iterable @(1..5) 2
2
3
4
5
6
#>
begin {
# `System.Collections.ArrayList.Add` is faster than `+=`
# ref: https://qiita.com/miyamiya/items/83127ae69d03f94d8443
$processed = New-Object System.Collections.ArrayList
$i = $start
}
process {
# NOTE: `$iterable` is an array whose Count is 1 when called as a filter.
foreach($element in $iterable) {
$value = $callback.InvokeWithContext(
$null,
@(
[psvariable]::new('i', $i),
[psvariable]::new('_', $element)
),
@($i, $iterable[$i])
)
if ($null -ne $value) {
$processed.Add($value) | Out-Null; # `System.Collections.ArrayList.Add` returns the index of the new item. By using `Out-Null`, its output is not shown in the shell.
}
$i += 1
}
if ($iterable.Count -eq 1) {
# In the context of the filter, only the element currently processed is returned.
$value;
} else {
# In the context of the function, the processed array is returned.
$processed;
}
}
end {
}
}
使い方
インストール: 上記の関数をプロファイルに追加してください。
プロファイルのパスは$PROFILE
に格納されています。
echo $PROFILE
# C:\Users\takeMe1010\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
# 何らかのエディタで開いて、上記の関数を追加してください。
# e.g. `code $PROFILE`
コールバック関数では$i
にindexが、$_
に各要素が入っています。
次の例のように、フィルターみたいに使えます。
$temp = @(1..5)
$temp | enumerate {Write-Output ($_ + $i)}
# 1
# 3
# 5
# 7
# 9
関数みたいに使うこともできます。
$temp = @(1..5)
enumerate {Write-Output ($_ + $i)} -iterable $temp
# 1
# 3
# 5
# 7
# 9
変数名を明示して無名関数(?)で受け取ることもできます。
この場合引数は必ず(index, element)
の順番です。
$temp = @(1..5)
$temp | enumerate {
>> param (
>> [Int32]$i,
>> [Int32]$value
>> )
>> Write-Output ($value + $i)
>> }
# 1
# 3
# 5
# 7
# 9
参考文献
- インスパイア元記事。
InvokeWithContext
便利だ...
-
+=
は遅いよという記事
- 配列をループしながらindexを取り出す方法の議論例。できるだけ簡単にやる方法が見当たらなかったので作った。