一直对LINQ感兴趣,正好PowerShell确实可以用,也写了不少相关的东西,那就另开这么一个文章,以后查也方便。
之前写过LINQ相关的代码会搬过来一些,稍微注意。
要是遇到需要重要的地方,哪怕跟LINQ并无关系,也会说一两句的。
环境
若未指明:
操作系统Win10 2004,PowerShell 7.1.0(现在的时点——2020/10/14——为rc.1),$PSCulture
和$PSUICulture
均为ja-JP
。
(2020/10/23)刚才更新了一下,成了rc.2。
参考
别人踩出来的路
- A Visual Lexicon of LINQ:可视化理解LINQ,算是ww
- High Performance PowerShell with LINQ:我寻思光是看这个也行w
文档
正文
Aggregate
这个在JS那边对应的可以是Array.prototype.reduce()
。
[Linq.Enumerable]::Aggregate( # Aggregate<TSource>(
[int[]] (1..100), # IEnumerable<TSource>,
[Func[int, int, int]] { # Func<TSource,TSource,TSource>
Param($s, $i)
$s + $i
}
) # )
# -> 5050
老小高斯了。
但写完了总觉得有什么地方不对:这么常见的操作,得有个Sum
吧。
Sum
[Linq.Enumerable]::Sum([int[]] (1..100))
# -> 5050
……你说我刚才用Aggregate
图什么www
再就是,虽然不提也罢,就是其中的参数必须明示类型,不然我看它自己找不到用哪个overload
方法。
Select
嗯,JS那边说就是map
。IE那边从版本9就开始支持这个了,自然IE 11也是OK的——躲不开的IE哟。
ConvertTo-Json (
[Linq.Enumerable]::Select( # Select<TSource,TResult>(
[int[]] (1..10), # IEnumerable<TSource>,
[Func[int, int]] { # Func<TSource,TResult>
Param($i)
$i * $i
}
) # )
# -> [1,4,9,16,25,36,49,64,81,100]
) -Compress
这个用JS写就是
JSON.stringify([1,2,3,4,5,6,7,8,9,10].map(i => i * i))
// -> [1,4,9,16,25,36,49,64,81,100]
很常用的东西,不需要说太多也行。
SelectMany
换句话说,LINQ的SelectMany
其实和我们常说的flatMap
(JS那边会用到)基本用法上是一个东西。
IE直到11都没有这个,注意一下。
那么干脆就用JS那边的用例试试看。参见:Array.prototype.flatMap()
$arr = [int[]] @(1, 2, 3, 4)
<# -- 1 -- #>
$result =
[Linq.Enumerable]::SelectMany( # SelectMany<TSource,TResult>(
[int[]] $arr, # IEnumerable<TSource>,
[Func[int, int[]]] { Param($i); @($i, ($i * 2)) } # Func<TSource,IEnumerable<TResult>>
) # )
ConvertTo-Json -InputObject $result -Compress
# -> [1,2,2,4,3,6,4,8]
<# -- 2 -- #>
$result =
[Linq.Enumerable]::SelectMany(
[int[]] $arr,
[Func[int, int[]]] { Param($i); @($i * 2) }
)
ConvertTo-Json -InputObject $result -Compress
# -> [2,4,6,8]
<# -- 3 -- #>
$result =
[Linq.Enumerable]::SelectMany(
[int[]] $arr,
[Func[int, int[][]]] { Param($i); @(,(,($i * 2))) } # 「@(,(,($i * 2)))」这样的写法在这里其实并无必要,毕竟Func的类型已经声明得非常充分了,哪怕写成「@($i * 2)」也可出正常结果。即便如此,还是要注意嵌套数组的表示方法。在此仅仅是抛砖引玉
)
ConvertTo-Json -InputObject $result -Compress
# -> [[2],[4],[6],[8]]
<# -- 之后的例子还是省略了吧 -- #>
至于嵌套数组:
# 我们可以:
ConvertTo-Json -InputObject @(,(,(1 * 2))) -Compress
# -> [[2]]
# 内部不需要计算值的话:
ConvertTo-Json -InputObject @(,(,2)) -Compress
# -> [[2]]
# 但是:
ConvertTo-Json -InputObject @(,(,1 * 2)) -Compress
# -> [[1,1]]
# 再稍微简单一点:
ConvertTo-Json -InputObject @(,1 * 2) -Compress
# -> [1,1]
# 嗯。总之当心
除了之前在例3里提到的(虽然现在无所谓)嵌套数组表示以外,还请注意ConvertTo-Json
的使用方法。
咱们接着例3的结果继续:
# PowerShell 5.1
$result | ConvertTo-Json -Compress
# -> [{"value":[2],"Count":1},{"value":[4],"Count":1},{"value":[6],"Count":1},{"value":[8],"Count":1}]
# PowerShell 7.0.3 & PowerShell 7.1.0-rc.1
$result | ConvertTo-Json -Compress
# -> [[2],[4],[6],[8]]
原因应该是管道针对数组的特别处理。啊,实际上并不是很清楚为什么会出现Count
,以后想起来了再去查。
这里有版本差异,而现在时点(2020/10/13),主流版本是PowerShell 5.1,为了避免不必要的麻烦,建议直接使用诸如ConvertTo-Json -InputObject $result -Compress
的办法,因为它的行为相对一致,在本例结果均为[[2],[4],[6],[8]]
,对用户来说又很理想。
说来,JS那边有flat
,简单说就是抹平一层嵌套。LINQ没有这东西,但实际上我们可以用x => x
的办法,但要注意类型:
ConvertTo-Json (
[Linq.Enumerable]::SelectMany(
[int[][]] @((,2),(,4),(,6),(,8)), # [[2],[4],[6],[8]]
[Func[[int[]], [int[]]]] { # 看着挺晕的,但遇到这种含有数组的类型声明不要用的情况,也只好对这数组再套一层方括号了
Param($x)
$x
}
)) -Compress
# -> [2,4,6,8]
至于JS那边,可以
JSON.stringify([[2],[4],[6],[8]].flat())
// -> [2,4,6,8]