1

I am trying to create a function to loop through json file and based upon specific condition met want to add the value of the json keys to array but not able to accomplish the same.

Following is the json:

$a1 = [    {
        "name": "sachin",
        "surname": "",
    },
    {
        "name": "Rajesh",
        "surname": "Mehta"

    }
]

and the function that i have created is below:

function addingkeyval
{
param ($key,
        [Array]$tobeaddedarr)

$a1 | Select-Object -Property name,surname | ForEach-Object {
    if($_.$key-eq "")
    {
    }
    else
    {     
    $tobeaddedarr +=$_.$key+","
    }
}

}

when i cal this function with the statement:

$surnamearr = @()
addingkeyval -key surname -tobeaddedarr $surnamearr

i want the output in the array $surnamearr as below
("Mehta")

somehow i am not able to add the value in the array can anyone help me in achieving the same

codehacker
  • 320
  • 2
  • 3
  • 16
  • Thanks for accepting - note that I've just tweaked the answer slightly, so that (hypothetical) input objects without a `.surname` property are filtered out as well. – mklement0 May 05 '20 at 12:04

1 Answers1

3
$surnamearr = ([string[]] (ConvertFrom-Json $a1).surname) -ne ''
  • ConvertFrom-Json $a1 converts the JSON array to an array of PowerShell custom objects (type [pscustomobject]).

  • (...).surname extracts the values of all .surname properties of the objects in the resulting array, using member-access enumeration.

  • Cast [string[]] ensures that the result is treated as an array (it wouldn't be, if the array in $a1 happened to contain just one element) and explicitly casts to strings, so that filtering nonempty values via -ne '' also works for input objects that happen to have no .surname property.

  • -ne '' filters out all empty-string elements from the resulting array (with an array as the LHS, comparison operators such as -ne act as array filters rather than returning a Boolean value).

The result is that $surnamearr is an array containing all non-empty .surname property values.


As for what you tried:

$tobeaddedarr +=$_.$key+","

This statement implicitly creates a new array, distinct from the array reference the caller passed via the $tobeaddedarr parameter variable - therefore, the caller never sees the change.

You generally cannot pass an array to be appended to in-place, because arrays are immutable with respect to their number of elements.

While you could pass an mutable array-like structure (such as [System.Collections.Generic.List[object]] instance, which you can append to with its .Add() method), it is much simpler to build a new array inside the function and output it.

Therefore, you could write your function as:

function get-keyval
{
  param ($key)

  # Implicitly output the elements of the array output by using this command.
  ([string[]] (ConvertFrom-Json $key).surname) -ne ''
}

$surnamearr = get-keyval surname

Note:

  • Outputting an array or a collection in PowerShell by default enumerates it: it sends its elements one by one to the output stream (pipeline).

    • While it is possible to output an array as a whole, via the unary form of ,, the array-construction operator ( , (([string[]] (ConvertFrom-Json $a1).surname) -ne '')), which improves performance, the problem is that the caller may not expect this behavior, especially not in a pipeline.
  • If you capture the output in a variable, such as $surnamearr here, PowerShell automatically collects the individual output objects in an [object[]] array for you; note, however, that a single output object is returned as-is, i.e. not wrapped in an array.

    • You can wrap @(...) around the function call to ensure that you always get an array
    • Alternatively, type-constrain the receiving variable: [array] $surnamearr = ..., which also creates an [object[]] array, or something like [string[]] $surnamearr = ..., which creates a strongly typed string array.
mklement0
  • 382,024
  • 64
  • 607
  • 775