遍历并追加hastable而不是添加到键

时间:2019-07-26 19:43:39

标签: rest powershell woocommerce woocommerce-rest-api

我正在遍历CSV文件,并使用ForEach-Object循环来获取信息以尝试更新Woocommerce上的in_stock状态,最终发生的是woocommerce只看到一个条目。我不是程序员,我仍在学习PowerShell,对于我一生,我只是不了解for循环的逻辑,并且它的输出正确。我知道它读取CSV中的条目,但我认为它只是覆盖先前的条目。 我遇到的另一个问题是将每个对象的in_stock值分别正确设置为true和false,如果一个为true,则所有false条目也都设置为true。我似乎无法弄清楚如何分配true |正确的错误。

我一直在使用PowerShell上的MS文档以及如何添加哈希表来查找PowerShell,但是我仍然找不到找到正确方向的答案或示例。我到目前为止已经在现场购买了PowerShell教程,但仍然找不到适当的方法。

$website = "https://www.mywebsite.com"
$params += @{ 
    type= @();
    name = @();
    SKU = @();
    catalog_visibility = @();
    regular_price = @();
    in_stock = @();
    categories = @();
}

$csv = Import-Csv C:\test\test-upload.csv
$csv | Select-Object -Property Type, SKU, Name, 'Visibility in catalog', 
'Tax status', 'In stock?', Stock, 'Backorders allowed?', 'Allow customer 
reviews?', 'Regular price', Categories | ForEach-Object{ 
$params.type += $_.type
$params.SKU += $_.SKU
$params.name += $_.name 
$params.catalog_visibility += $_.'Visibility in catalog'
$params.categories += $_.Categories
$params.regular_price += $_.'Regular price'
$params.in_stock += $_.'In stock?'
if ($params.in_stock = 0) {$params.in_stock -replace 0, $false} 
 elseif($params.in_stock = 1) {$params.in_stock -replace 1, $true}   
}

foreach($key in $params.keys){

    Write-Output $params[$key]
} 

我正在寻找类似的东西

    {
      "name": "part 1",
      "type": "simple",
      "SKU": "0001",
      "regular_price": "21.99",
      "in_stock: false",
      "categories: category 1",
      "catalog_visibility": "hidden",
    },
    {
      "name": "part 2",
      "type": "simple",
      "SKU": "0002",
      "regular_price": "11.99",
      "in_stock: true",
      "categories: category 2",
      "catalog_visibility": "hidden",
    }

我真正得到的是

    {
      "name": "part 1 part 2",
      "type": "simple simple ",
      "SKU": "0001 0002",
      "regular_price": "21.99 11.99",
      "in_stock: true true",
      "categories: category 1 category 1",
      "catalog_visibility": "hidden hidden",
    }

如果有人能指出正确的方向并给我一些最佳实践的提示,我将不胜感激

2 个答案:

答案 0 :(得分:0)

因此,您正在做大量的+=尝试创建一个数组,但是这样做的级别是错误的。您要做的是为CSV中的每个项目创建一个哈希表(或者很可能是PSCustomObject),并将它们捕获为对象数组(它们是哈希表对象或PSCustomObject对象)。因此,让我们尝试对事物进行一些重组以实现此目的。我放弃了模板,我们不在乎,无论如何我们都在为每个对象定义模板。我将为ForEach-Object循环中的每个项目输出一个哈希表,并在$params中捕获它。这应该给您想要的结果。

$website = "https://www.mywebsite.com"

$csv = Import-Csv C:\test\test-upload.csv
$params = $csv | Select-Object -Property Type, SKU, Name, 'Visibility in catalog', 'Tax status', 'In stock?', Stock, 'Backorders allowed?', 'Allow customer reviews?', 'Regular price', Categories | ForEach-Object{ 
    @{
        type = $_.type
        SKU = $_.SKU
        name = $_.name 
        catalog_visibility = $_.'Visibility in catalog'
        categories = $_.Categories
        regular_price = $_.'Regular price'
        in_stock = [boolean][int]($_.'In stock?')
    }
}

答案 1 :(得分:0)

由于您是编程的新手,让我们来谈谈数组和哈希表。

数组就像列表(有时也称为列表)一样,特别是按位置排序的列表。

哈希表是 dictionary 的一种,其中您具有对应于值的Key。

在PowerShell中,用于创建数组的语法为@()(空的,可能包含项),用于创建哈希表的语法为@{}(也为空,可能包含值)。

您没有显示$params的初始定义,但是基于其余代码,我将假定它是这样的:

$params = @()

然后,您拥有这个:

$params += @{ 
    type= @();
    name = @();
    SKU = @();
    catalog_visibility = @();
    regular_price = @();
    in_stock = @();
    categories = @();
}

因此,这意味着您拿了$params数组,并向其中添加了一个新项目。新项目是您在此处定义的哈希表文字。您添加的所有名称,例如typenameSKU等,都是键。

根据您期望的输出,它确实看起来像您想要哈希表数组,所以我认为这部分是正确的。

但是请注意,您分配给它们的都是空数组。这很好奇,因为您显示为所需输出的每个哈希表的键值都是奇异值,所以我认为这是一个问题,实际上,它使问题真正所在的区域变得模糊了。

因此,让我们跳到使用此模式的循环主体:

$params.type += $_.type
$params.SKU += $_.SKU
$params.name += $_.name 
$params.catalog_visibility += $_.'Visibility in catalog'
$params.categories += $_.Categories
$params.regular_price += $_.'Regular price'
$params.in_stock += $_.'In stock?'

请记住,$params是一个数组,因此您应该在其中包含从0开始的项,例如$params[0]$params[1]等。要在其中更改第二个哈希表的SKU数组,则可以使用$params[1].SKU$params[1]['SKU']

但是您正在做的只是$params.SKU。在许多语言中,甚至在v3之前的PowerShell中,这都会引发错误。数组本身没有名为SKU的属性。在PowerShell v3中,虽然增强了点.运算符以允许其自检到数组中并返回给定名称的每个项目的属性,即:

$a = @('apple','orange','pear')
$a.PadLeft(10,'~')

和我们做的一样:

$a = @('apple','orange','pear')
$a | ForEach-Object { $_.PadLeft(10,'~') }

这非常有用,但可能会使您感到困惑。

所以回到您的对象,$params是一个数组,到目前为止,其中只有一个哈希表。并且在您的循环中,您没有向$params添加任何内容。

相反,您要求提供$params.SKU,在这种情况下,它将是数组中每个哈希表的SKU,但是只有一个哈希表,因此您只能得到一个SKU。 / p>

然后将您添加到SKU本身:

$params.SKU += $_.SKU

这是最初将SKU设置为空数组的部分隐藏了您的问题。如果SKU是一个字符串,则将失败,因为字符串不支持+=,但是由于它是一个数组,因此您要使用这个新值,并将其添加到 array < / em>个SKU,它们是您要处理的单个哈希表的值。


从这里去哪里

  1. 在这种情况下,请勿使用数组作为值
  2. 在循环的每次迭代中
  3. 创建一个 new 哈希表,然后将该新哈希表添加$params数组

让我们看看:

$params = @()

$csv = Import-Csv C:\test\test-upload.csv
$csv | Select-Object -Property Type, SKU, Name, 'Visibility in catalog', 
'Tax status', 'In stock?', Stock, 'Backorders allowed?', 'Allow customer 
reviews?', 'Regular price', Categories | ForEach-Object { 
$params += @{  # new hashtable here
    type = $_.type
    SKU = $_.SKU
    name = $_.name 
    catalog_visibility = $_.'Visibility in catalog'
    categories = $_.Categories
    regular_price = $_.'Regular price'
  }
}

这是您遇到的主要问题,由于我将分别解释该逻辑,因此我省略了库存部分。


$params.in_stock = $_.'In stock?'
if ($params.in_stock = 0) {$params.in_stock -replace 0, $false} 
 elseif($params.in_stock = 1) {$params.in_stock -replace 1, $true}   
}

您的CSV似乎有一个In stock?列,可以是01来表示错误/正确。

我要解决的第一件事是PowerShell中的=始终是赋值的。测试是否相等-eq,因此:

$params.in_stock = $_.'In stock?'
if ($params.in_stock -eq 0) {$params.in_stock -replace 0, $false} 
 elseif($params.in_stock -eq 1) {$params.in_stock -replace 1, $true}   
}

接下来,我们来讨论真/假值;它们简称为Boolean或bool,通常应使用此数据类型来表示它们。每当您进行比较时,例如$a -eq 5,您都会返回布尔值。

强烈支持将其他类型转换为bool,例如,如果您希望将数字评估为bool,0为false,而所有其他值均为true。对于字符串,$null值或空字符串为false,其他所有值为true。请注意,如果您的字符串"0" true ,因为该字符串具有值。

这也意味着数字0与字符串'0'不同,但是PowerShell确实尝试在类型之间进行转换,通常尝试将右侧的类型转换为左侧的类型。比较,因此PowerShell会告诉您0 -eq '0'是正确的(与'0' -eq 0相同)。

根据您的情况,从CSV读取时,这些值将最终以字符串形式出现,但是由于上述原因,您的相等性测试仍然可以正常工作(值得了解详细信息)。

尽管使用-replace的问题在于它是一个字符串操作,所以即使它起作用,您也将最终得到布尔值的字符串表示,而不是实际的布尔值,甚至尽管您曾说过直接使用$true$false(这又是由于类型转换; -replace在那里需要一个字符串,但PowerShell会将bool转换为字符串才能满足要求)。

因此,经过冗长的解释之后,这才是有意义的:

$params.in_stock = $_.'In stock?'
if ($params.in_stock -eq 0) {
    $params.in_stock = $false
} elseif($params.in_stock -eq 1) {
    $params.in_stock -eq $true
}

实际上,elseif不是必需的,因为您只能有2个值:

$params.in_stock = $_.'In stock?'
if ($params.in_stock -eq 0) {
    $params.in_stock = $false
} else {
    $params.in_stock -eq $true
}

尽管如此,我们甚至可以使用转换完全不需要条件转换。记住我说过的关于将字符串转换为数字,以及将数字转换为布尔的说法。

0 -as [bool]    # gives false
"0" -as [bool]  # gives true (whoops)
"0" -as [int]   # gives the number 0
"0" -as [int] -as [bool]  # false!

现在,我们可以这样做:

$params.in_stock = $_.'In stock?' -as [int] -as [bool]

酷!让我们将其放回其他代码中:

$params = @()

$csv = Import-Csv C:\test\test-upload.csv
$csv | Select-Object -Property Type, SKU, Name, 'Visibility in catalog', 
'Tax status', 'In stock?', Stock, 'Backorders allowed?', 'Allow customer 
reviews?', 'Regular price', Categories | ForEach-Object { 
$params += @{  # new hashtable here
    type = $_.type
    SKU = $_.SKU
    name = $_.name 
    catalog_visibility = $_.'Visibility in catalog'
    categories = $_.Categories
    regular_price = $_.'Regular price'
    in_stock = $_.'In stock?' -as [int] -as [bool]
  }
}

更深的潜水!

管道:您正在执行一些调用,例如Import-Csv调用,并将其输出分配给变量,然后将该变量传递给另一个命令。很好,这没错,但是您也可以像下面这样直接将第一个命令的输出通过管道传递给第二个命令:

$params = @()

Import-Csv C:\test\test-upload.csv |
    Select-Object -Property Type, SKU, Name, 'Visibility in catalog', 
'Tax status', 'In stock?', Stock, 'Backorders allowed?', 'Allow customer 
reviews?', 'Regular price', Categories | 
    ForEach-Object { 
        $params += @{  # new hashtable here
            type = $_.type
            SKU = $_.SKU
            name = $_.name 
            catalog_visibility = $_.'Visibility in catalog'
            categories = $_.Categories
            regular_price = $_.'Regular price'
            in_stock = $_.'In stock?' -as [int] -as [bool]
         }
     }

我更新了一下格式,以显示可以在管道|之后使用换行符,看起来更干净一些。

关于Select-Object:其目的是获取具有特定属性集的对象,并带给您具有更有限属性(或有时具有全新属性)的新对象(它在更改对象数量或以其他与当前不相关的方式过滤数组)。

但是我提出来了,因为您选择的所有属性(列)都是按名称命名的,因此必须存在于输入对象上。而且由于以后您直接引用每个对象而不是显示整个对象,因此没有理由使用Select-Object来过滤属性,从而可以删除整个调用:

$params = @()

Import-Csv C:\test\test-upload.csv |
    ForEach-Object { 
        $params += @{  # new hashtable here
            type = $_.type
            SKU = $_.SKU
            name = $_.name 
            catalog_visibility = $_.'Visibility in catalog'
            categories = $_.Categories
            regular_price = $_.'Regular price'
            in_stock = $_.'In stock?' -as [int] -as [bool]
        }
    }

好!看起来苗条。

关于数组和+=。老实说,在大多数情况下都可以,但是您应该知道,每次执行此操作时,实际上都会创建一个新数组,并且将所有原始项目和新项目复制到它。这不能扩展,但是在大多数情况下还是可以的。

您还应该知道,管道的输出(如任何命令,主脚本代码或ForEach-Object的主体都被发送到管道中的下一个命令(或退出管道)。左侧(如果没有其他内容)。它可以是任意数量的项目,并且您可以使用分配来获取所有这些值,例如:

$a = Get-ChildItem $env:HOME  # get all of items in the directory

$a将是一个数组,如果有多个项,并且在处理过程中不会连续创建和销毁数组。

那么这与您有什么关系?这意味着您不必将$params创建为空数组并追加到其上,只需在每次循环迭代中返回新的哈希表,然后将管道的输出分配给$params就可以了!

$params = Import-Csv C:\test\test-upload.csv |
    ForEach-Object { 
        @{  # new hashtable here
            type = $_.type
            SKU = $_.SKU
            name = $_.name 
            catalog_visibility = $_.'Visibility in catalog'
            categories = $_.Categories
            regular_price = $_.'Regular price'
            in_stock = $_.'In stock?' -as [int] -as [bool]
        } # output is implicit
    }

现在我们将您的脚本放到一个管道中(您可以将其设置为单行,但我更喜欢多行格式)。