Flatten array in PowerShell - collections

Assume we have:
$a = #(1, #(2, #(3)))
I would like to flatten $a to get #(1, 2, 3).
I have found one solution:
#($a | % {$_}).count
But maybe there is a more elegant way?

Piping is the correct way to flatten nested structures, so I'm not sure what would be more "elegant". Yes, the syntax is a bit line-noisy looking, but frankly quite serviceable.
2020 Edit
The recommended syntax these days is to expand % to ForEach-Object. A bit more verbose but definitely more readable:
#($a | ForEach-Object {$_}).count

Same code, just wrapped in function:
function Flatten($a)
{
,#($a | % {$_})
}
Testing:
function AssertLength($expectedLength, $arr)
{
if($ExpectedLength -eq $arr.length)
{
Write-Host "OK"
}
else
{
Write-Host "FAILURE"
}
}
# Tests
AssertLength 0 (Flatten #())
AssertLength 1 (Flatten 1)
AssertLength 1 (Flatten #(1))
AssertLength 2 (Flatten #(1, 2))
AssertLength 2 (Flatten #(1, #(2)))
AssertLength 3 (Flatten #(1, #(2, #(3))))

This problem is probably most elegantly resolved with the .ForEach() array method introduced in Powershell v4.0. Performance-wise it has the advantage of not needing to construct a pipeline, so in some cases it might perform better.
> $a.ForEach({$_}).Count
3
If you already have a pipeline, the easiest way to flatten an array is to pipe it through Write-Output:
> $b = $a | Write-Output
> $b.Count
3

There are examples of nested arrays where piping to ForEach-Object simply can't handle them.
For example, given our nested array:
$toUnroll = #(#(0,1),#(2,3),#(#(4,#(5,6)),#(7,8),9),10)
If we attempt to pipe to ForEach-Object, the result would be:
PS /> $toUnroll | ForEach-Object { $_ }
0
1
2
3
4
Length : 2
LongLength : 2
Rank : 1
SyncRoot : {5, 6}
IsReadOnly : False
IsFixedSize : True
IsSynchronized : False
Count : 2
7
8
9
10
Write-Output is also not able to handle the unrolling:
$toUnroll | Write-Output | ForEach-Object GetType
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Object[] System.Array
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
True True Int32 System.ValueType
Below we can see some examples on how we can handle the flattening of these nested arrays including a one-liner anonymous function.
Recursive Function
This technique unrolls our array in order.
function RecursiveUnroll {
[cmdletbinding()]
param(
[parameter(Mandatory, ValueFromPipeline)]
[object[]] $Unroll
)
process {
foreach($item in $Unroll) {
if($item -is [object[]]) {
RecursiveUnroll -Unroll $item
continue
}
$item
}
}
}
RecursiveUnroll -Unroll $toUnroll
# Results in an array from 0 to 10
One-liner anonymous function:
The logic for this script block is the exact same as the function demonstrated above.
$toUnroll | & { process { if($_ -is [object[]]) { return $_ | & $MyInvocation.MyCommand.ScriptBlock }; $_ }}
Recursive Class Method (can be static or instance)
Same as the recursive function example, we can expect the array to keep it's order. We can add that this technique should be faster than the function since parameter binding is much faster for methods.
class Unroller {
[object[]] $Array
Unroller() { }
Unroller([object[]] $Array) {
$this.Array = $Array
}
static [object] Unroll([object[]] $Array) {
$result = foreach($item in $Array) {
if($item -is [object[]]) {
[Unroller]::Unroll($item)
continue
}
$item
}
return $result
}
[object] Unroll () {
return [Unroller]::Unroll($this.Array)
}
}
# Instantiating and using using the instance method of our class:
$instance = [Unroller] $toUnroll
$instance.Unroll()
# Results in an array from 0 to 10
# Using the static method of our class, no need to instantiate:
[Unroller]::Unroll($toUnroll)
# Results in an array from 0 to 10
Queue
This technique should be the fastest one, the downside is that we cannot expect an ordered array.
$queue = [Collections.Queue]::new()
$queue.Enqueue($toUnroll)
while($queue.Count) {
foreach($item in $queue.Dequeue()) {
if($item -is [object[]]) {
$queue.Enqueue($item)
continue
}
$item
}
}
# Using our given nested array as an example we can expect
# a flattened array with the following order:
# 10, 0, 1, 2, 3, 9, 4, 7, 8, 5, 6
Stack
Lastly, using a Stack we can ensure that the order is preserved, this technique is also very efficient.
$stack = [Collections.Stack]::new()
$stack.Push($toUnroll)
$result = while($stack.Count) {
foreach($item in $stack.Pop()) {
if($item -is [object[]]) {
[array]::Reverse($item)
$stack.Push($item)
continue
}
$item
}
}
[array]::Reverse($result)
$result

You can use .NET's String.Join method.
[String]::Join("",$array)

Related

How can I create new map with new values but same keys from an existing map?

I have an existing map in Groovy.
I want to create a new map that has the same keys but different values in it.
Eg.:
def scores = ["vanilla":10, "chocolate":9, "papaya": 0]
//transformed into
def preference = ["vanilla":"love", "chocolate":"love", "papaya": "hate"]
Any way of doing it through some sort of closure like:
def preference = scores.collect {//something}
You can use collectEntries
scores.collectEntries { k, v ->
[ k, 'new value' ]
}
An alternative to using a map for the ranges would be to use a switch
def grade = { score ->
switch( score ) {
case 10..9: return 'love'
case 8..6: return 'like'
case 5..2: return 'meh'
case 1..0: return 'hate'
default : return 'ERR'
}
}
scores.collectEntries { k, v -> [ k, grade( v ) ] }
Nice, functional style solution(including your ranges, and easy to modify):
def scores = [vanilla:10, chocolate:9, papaya: 0]
// Store somewhere
def map = [(10..9):"love", (8..6):"like", (5..2):"meh", (1..0):"hate"]
def preference = scores.collectEntries { key, score -> [key, map.find { score in it.key }.value] }
// Output: [vanilla:love, chocolate:love, papaya:hate]
def scores = ["vanilla":10, "chocolate":9, "papaya": 0]
def preference = scores.collectEntries {key, value -> ["$key":(value > 5 ? "like" : "hate")]}
Then the result would be
[vanilla:like, chocolate:like, papaya:hate]
EDIT: If you want a map, then you should use collectEntries like tim_yates said.

$all Operator in Casbah

Could you please give me an example of how to use the $all operator for my two elemMatch objects?
val elemMatch1 = foo()
val elemMatch2 = bar()
How can I perform the query of $all( elemMatch1, elemMatch2) (all documents where elemMatch1 and elemMatch2)?
I'm not sure how much sense it makes to mix $all and $elemMatch but from the docs $all follows:
{ <field>: { $all: [ <value> , <value1> ... ] }
$elemMatch follows:
{ array: { $elemMatch: <document> } }
Unfortunately, the casbah DSL won't help there as $all requires a list and $elemMatch expects a string, so you have to manually have to build the document:
import com.mongodb.casbah.Imports._
val coll = MongoClient()("test")("testB")
coll += MongoDBObject("array" -> List(
MongoDBObject("value1" -> 1, "value2" -> 0),
MongoDBObject("value1" -> 1, "value2" -> 2)
))
val elemMatch = MongoDBObject("$elemMatch" -> MongoDBObject("value1" -> 1, "value2" -> 2))
val query = "array" $all List(elemMatch)
coll.find(query).count

For loop in R with key value

What is the equivalent way in R to
foreach ($arr as $key => $value) {
echo "Key: $key; Value: $value<br />\n";
}
that means
arr<-c(a=1,b=2,c=3)
key<-names(arr)
val<-arr
for(i in 1:length(arr)){
print(paste(key[i],val[i]))
}
Assuming var is a list of key value pairs, a more generic foreach loop can be achieved with the following snippet:
for(key in names(var)){
value<-var[key]
print(paste(key,'=',value))
}
With the foreach you can write:
foreach(key=names(arr), val=arr) %do% print(paste(key,val))
And you can define your own forkeyval function:
forkeyval = function(arr, .combine=function(...){NULL}, ...) {
foreach(key=names(arr), val=arr, .combine=.combine, ...) }
Which lets you write:
forkeyval(arr) %do% print(paste(key,val)
R likes to vectorize things. You can do:
sprintf("Key: %s; Value: %s", names(arr), arr)
# [1] "Key: a; Value: 1" "Key: b; Value: 2" "Key: c; Value: 3"
Or for a nicer output, pass it through cat:
cat(sprintf("Key: %s; Value: %s", names(arr), arr), sep = "\n")
# Key: a; Value: 1
# Key: b; Value: 2
# Key: c; Value: 3
You can also use the kv() from the kv package. It is exceedingly light weight and departs very little from base R syntax.
for( . in kv(arr) ) {
cat( "Key:", .$k, "Value:", .$v, "<br />\n" )
}
Disclosure: I wrote kv.

Groovy Collections: count weirdness with Strings

I am trying to count string values in a member of an object. I have tried three ways, but only one works. I am fine with the one that works, but I can't understand why the others fail. Here's the code:
void testCount() {
TestObj a = new TestObj()
TestObj b = new TestObj()
TestObj c = new TestObj()
a.s = "one"
b.s = "two"
c.s = "two"
def list = [a, b, c]
def count = 0
list.each{
if (it.s.equals("two"))
count++
}
assertTrue("each test failed", count == 2)
assertTrue("collectAll test failed", list.collectAll{ it.s.equals("two")}.size() == 2)
assertTrue("count test failed", list.count{ it.s.equals("two")} == 2)
}
I would expect the Closures passed to collectAll and count to do the same thing I'm doing in my each method. But in the case of collectAll it returns all 3 of the objects and in the case of count it always returns 0.
What am I missing?
collectAll is recursively going through your list, and returning a boolean (as that is what your closure returns for each element in the List)...
So, you get [ false, true, true ], which has 3 elements...
For count,
list.count{ it.s == "two" }
Returns 2 (as expected)
btw: you can do it.s == 'two' in groovy.. no need for all the .equals( "two" )
Edit... Example for count:
class TestObj {
String s
}
list = [ new TestObj( s:'one' ), new TestObj( s:'two' ), new TestObj( s:'two' ) ]
println( list.count { it.s == 'two' } )
Prints 2 for me...
edit 2
Found the cause (from comment below), count didn't accept a closure as a parameter till 1.8 so you'll be calling the object version which will tell you how many times an instance of the closure exists in the list (which is none, as it says)

Correct way to access Multi-Dimensional Array with string indexes in Lua?

I'm trying to have a good access to multi-dimensional arrays with string indexes in Lua, here's basically what I'm trying to do:
rules =
{
{"S_RIGHT", "A_STOP", "S_RESULT"},
}
matrix = {}
for _,v in pairs(rules) do
if( matrix[ v[1] ] == nil ) then
matrix[ v[1] ] = {}
end
matrix[ v[1] ][ v[2] ] = v[3]
end
-- results in error ( attempt to index field 'S_NO' a nil value)
var = matrix["S_NO"]["S_RESULT"]
assert(var == nil, "Var should be nil")
A way to do it but quite verbose is:
var = matrix["S_NO"]
if var ~= nil then
var = var["S_RESULT"]
end
assert(var == nil, "Var should be nil")
Is there a way to make the first case to work ? ( less verbose )
Ok,
Found the answer.
If matrix is going to be read-only a correct approach would be:
local empty = {}
setmetatable(matrix, {__index=function() return empty end})
If I would like to allow writes and it's specifically two levels of tables, I could do:
setmetatable(matrix, {__index=function(t,k) local new={} rawset(t,k,new) return new end}
Hope this helps!

Resources