LoginSignup
0
0

Powershellでも(手軽に)enumerateしたい

Last updated at Posted at 2023-06-15

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を取り出す方法の議論例。できるだけ簡単にやる方法が見当たらなかったので作った。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0