Retrieve a random number of items from a randomized array

1.6k views Asked by At

In Powershell v4, I need to read in the contents of a file that contains SKUs and associated product names (likely comma-delimited), randomize those entries, then display a random number of the resulting SKUs and names. For instance, there may be a file with 12 product names and associated SKUs in it. On the first run, it might result in something like this:

SKU: 123456, ProductName: Apples
SKU: 789012, ProductName: Oranges

...and the next time it's run, it might result in this:

SKU: 524367, ProductName: Bananas
SKU: 235023, ProductName: Avocados
SKU: 123456, ProductName: Apples
SKU: 543782, ProductName: Peaches

...and so on. The number of entries in the list of SKUs and products could be as large as twenty thousand, but I'd only need to display between one and fifty SKUs and products at a time. Is there a way to accomplish this within Powershell?

I'm new to Powershell, but I have the basics down; thus far, I have mostly just done a lot of Get-WMIObject commands or interacting with processes and services. Forgive my wordiness with the remarks below; I'm trying to make my goal and process as plain as possible.

# Create the arrays of SKUs and Product names (I currently have them in two separate files,
# but can combine them easily - I separated them during my experimentation with this
# problem).
$SKU_Array = Get-Content $ScriptFolder\SKUs.txt
$Product_Array = Get-Content $ScriptFolder\Product_Names.txt

# There are 12 items in the sample files, but .Length doesn't count zero, so we subtract
# one index number from the array so that we don't end up calling on an empty item.
$NumberOfSKUs = $SKU_Array.Length - 1
$NumberOfProductNames = $Product_Array.Length - 1

# Pick a random item from the array using the whole range of index numbers (I stopped
# worrying about the SKUs, and was just concentrating on getting the product names
# portion working here).
$RandomProductName = 0..$NumberOfProductNames | Get-Random

# Display the item picked in the previous line; thus far, I haven't figured out how to
# accomplish the rest of my goal.
$Product_Array[$RandomProductName]
2

There are 2 answers

0
Ansgar Wiechers On BEST ANSWER

I would put the SKUs and product names in a CSV:

SKU,ProductName
524367,Bananas
235023,Avocados
123456,Apples
543782,Peaches
789012,Oranges

You can get a random subset of 1 to 50 elements from a CSV like this (as suggested by @MikeShepard):

$products = Import-Csv 'C:\path\to\products.csv'

$num = (Get-Random -Minimum 0 -Maximum 50) + 1

$products | Get-Random -Count $num

If you also want to be able to access the product list by SKU you could read the CSV into a hashtable and modify the above code like this:

$products = @{}
Import-Csv 'C:\path\to\products.csv' | % {
  $products[$_.SKU] = $_.ProductName
}

$num = (Get-Random -Minimum 0 -Maximum 50) + 1

$products.Keys | Get-Random -Count $num | % { $products[$_] }
3
briantist On

Let's start by combining the SKUs and product names into an object, then get a random number between 1 and the total number of items, then that many times we'll get a random index:

$SKU_Array = Get-Content $ScriptFolder\SKUs.txt
$Product_Array = Get-Content $ScriptFolder\Product_Names.txt

# Make sure they're the same length
if ($SKU_Array.Length -ne $Product_Array.Length) {
    throw "Mismatch in the length of the SKU and Product Name files."
}
$ItemCount = $SKU_Array.Length
$MaxIndex = $ItemCount - 1

$Items = (0..$MaxIndex) | ForEach-Object {
    New-Object PSObject -Property @{
        Name = $Product_Array[$_]
        SKU = $SKU_Array[$_]
    }
}

# How many items to return this time?
$RndCount = Get-Random -Minimum 1 -Maximum $ItemCount

1..$RndCount | ForEach-Object {
    $RndIndex = 0..$MaxIndex | Get-Random
    $Items[$RndIndex]
}

Note that while this will get a random number of items, it can repeat. For example, it might return the same product (say, Apples), 12 times.

To make sure you only get each product once, we might change the end of that code to be like this:

# How many items to return this time?
$RndCount = Get-Random -Minimum 1 -Maximum $ItemCount
$RndItems = @()

while ($RndItems.Count -lt $RndCount)
    $RndIndex = 0..$MaxIndex | Get-Random
    if ($RndItems -notcontains $Items[$RndIndex]) {
        $RndItems += $Items[$RndIndex]
    }
}

$RndItems