質問するログイン新規登録

回答編集履歴

1

追記

2018/03/10 06:06

投稿

Zuishin
Zuishin

スコア28675

answer CHANGED
@@ -2,4 +2,214 @@
2
2
  SQL は使ったことがありますか?
3
3
  マイクロソフトの PowerShell チームのブログに使い回しできる関数がありますのでこれを使ってみてください。
4
4
 
5
- [Join-Object](https://blogs.msdn.microsoft.com/powershell/2012/07/13/join-object/)
5
+ [Join-Object](https://blogs.msdn.microsoft.com/powershell/2012/07/13/join-object/)
6
+
7
+ ###追記
8
+ カレントディレクトリに user.csv と machine.csv があるときに result.csv に書き込みます。
9
+ result.csv が既にあるとエラーになります。
10
+
11
+ ```
12
+ function AddItemProperties($item, $properties, $output) {
13
+ if ($item -ne $null) {
14
+ foreach ($property in $properties) {
15
+ $propertyHash = $property -as [hashtable]
16
+ if ($propertyHash -ne $null) {
17
+ $hashName = $propertyHash["name"] -as [string]
18
+ if ($hashName -eq $null) {
19
+ throw "there should be a string Name"
20
+ }
21
+
22
+ $expression = $propertyHash["expression"] -as [scriptblock]
23
+ if ($expression -eq $null) {
24
+ throw "there should be a ScriptBlock Expression"
25
+ }
26
+
27
+ $_ = $item
28
+ $expressionValue = & $expression
29
+
30
+ $output | add-member -MemberType "NoteProperty" -Name $hashName -Value $expressionValue
31
+ }
32
+ else {
33
+ # .psobject.Properties allows you to list the properties of any object, also known as "reflection"
34
+ foreach ($itemProperty in $item.psobject.Properties) {
35
+ if ($itemProperty.Name -like $property) {
36
+ $output | add-member -MemberType "NoteProperty" -Name $itemProperty.Name -Value $itemProperty.Value
37
+ }
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+
45
+ function WriteJoinObjectOutput($leftItem, $rightItem, $leftProperties, $rightProperties, $Type) {
46
+ $output = new-object psobject
47
+
48
+ if ($Type -eq "AllInRight") {
49
+ # This mix of rightItem with LeftProperties and vice versa is due to
50
+ # the switch of Left and Right arguments for AllInRight
51
+ AddItemProperties $rightItem $leftProperties $output
52
+ AddItemProperties $leftItem $rightProperties $output
53
+ }
54
+ else {
55
+ AddItemProperties $leftItem $leftProperties $output
56
+ AddItemProperties $rightItem $rightProperties $output
57
+ }
58
+ $output
59
+ }
60
+
61
+ <#
62
+ .Synopsis
63
+ Joins two lists of objects
64
+ .DESCRIPTION
65
+ Joins two lists of objects
66
+ .EXAMPLE
67
+ Join-Object $a $b "Id" ("Name","Salary")
68
+ #>
69
+ function Join-Object {
70
+ [CmdletBinding()]
71
+ [OutputType([int])]
72
+ Param
73
+ (
74
+ # List to join with $Right
75
+ [Parameter(Mandatory = $true,
76
+ Position = 0)]
77
+ [object[]]
78
+ $Left,
79
+
80
+ # List to join with $Left
81
+ [Parameter(Mandatory = $true,
82
+ Position = 1)]
83
+ [object[]]
84
+ $Right,
85
+
86
+ # Condition in which an item in the left matches an item in the right
87
+ # typically something like: {$args[0].Id -eq $args[1].Id}
88
+ [Parameter(Mandatory = $true,
89
+ Position = 2)]
90
+ [scriptblock]
91
+ $Where,
92
+
93
+ # Properties from $Left we want in the output.
94
+ # Each property can:
95
+ # – Be a plain property name like "Name"
96
+ # – Contain wildcards like "*"
97
+ # – Be a hashtable like @{Name="Product Name";Expression={$_.Name}}. Name is the output property name
98
+ # and Expression is the property value. The same syntax is available in select-object and it is
99
+ # important for join-object because joined lists could have a property with the same name
100
+ [Parameter(Mandatory = $true,
101
+ Position = 3)]
102
+ [object[]]
103
+ $LeftProperties,
104
+
105
+ # Properties from $Right we want in the output.
106
+ # Like LeftProperties, each can be a plain name, wildcard or hashtable. See the LeftProperties comments.
107
+ [Parameter(Mandatory = $true,
108
+ Position = 4)]
109
+ [object[]]
110
+ $RightProperties,
111
+
112
+ # Type of join.
113
+ # AllInLeft will have all elements from Left at least once in the output, and might appear more than once
114
+ # if the where clause is true for more than one element in right, Left elements with matches in Right are
115
+ # preceded by elements with no matches. This is equivalent to an outer left join (or simply left join)
116
+ # SQL statement.
117
+ # AllInRight is similar to AllInLeft.
118
+ # OnlyIfInBoth will cause all elements from Left to be placed in the output, only if there is at least one
119
+ # match in Right. This is equivalent to a SQL inner join (or simply join) statement.
120
+ # AllInBoth will have all entries in right and left in the output. Specifically, it will have all entries
121
+ # in right with at least one match in left, followed by all entries in Right with no matches in left,
122
+ # followed by all entries in Left with no matches in Right.This is equivallent to a SQL full join.
123
+ [Parameter(Mandatory = $false,
124
+ Position = 5)]
125
+ [ValidateSet("AllInLeft", "OnlyIfInBoth", "AllInBoth", "AllInRight")]
126
+ [string]
127
+ $Type = "OnlyIfInBoth"
128
+ )
129
+
130
+ Begin {
131
+ # a list of the matches in right for each object in left
132
+ $leftMatchesInRight = new-object System.Collections.ArrayList
133
+
134
+ # the count for all matches
135
+ $rightMatchesCount = New-Object "object[]" $Right.Count
136
+
137
+ for ($i = 0; $i -lt $Right.Count; $i++) {
138
+ $rightMatchesCount[$i] = 0
139
+ }
140
+ }
141
+
142
+ Process {
143
+ if ($Type -eq "AllInRight") {
144
+ # for AllInRight we just switch Left and Right
145
+ $aux = $Left
146
+ $Left = $Right
147
+ $Right = $aux
148
+ }
149
+
150
+ # go over items in $Left and produce the list of matches
151
+ foreach ($leftItem in $Left) {
152
+ $leftItemMatchesInRight = new-object System.Collections.ArrayList
153
+ $null = $leftMatchesInRight.Add($leftItemMatchesInRight)
154
+
155
+ for ($i = 0; $i -lt $right.Count; $i++) {
156
+ $rightItem = $right[$i]
157
+
158
+ if ($Type -eq "AllInRight") {
159
+ # For AllInRight, we want $args[0] to refer to the left and $args[1] to refer to right,
160
+ # but since we switched left and right, we have to switch the where arguments
161
+ $whereLeft = $rightItem
162
+ $whereRight = $leftItem
163
+ }
164
+ else {
165
+ $whereLeft = $leftItem
166
+ $whereRight = $rightItem
167
+ }
168
+
169
+ if (Invoke-Command -ScriptBlock $where -ArgumentList $whereLeft, $whereRight) {
170
+ $null = $leftItemMatchesInRight.Add($rightItem)
171
+ $rightMatchesCount[$i]++
172
+ }
173
+
174
+ }
175
+ }
176
+
177
+ # go over the list of matches and produce output
178
+ for ($i = 0; $i -lt $left.Count; $i++) {
179
+ $leftItemMatchesInRight = $leftMatchesInRight[$i]
180
+ $leftItem = $left[$i]
181
+
182
+ if ($leftItemMatchesInRight.Count -eq 0) {
183
+ if ($Type -ne "OnlyIfInBoth") {
184
+ WriteJoinObjectOutput $leftItem $null $LeftProperties $RightProperties $Type
185
+ }
186
+
187
+ continue
188
+ }
189
+
190
+ foreach ($leftItemMatchInRight in $leftItemMatchesInRight) {
191
+ WriteJoinObjectOutput $leftItem $leftItemMatchInRight $LeftProperties $RightProperties $Type
192
+ }
193
+ }
194
+ }
195
+
196
+ End {
197
+ #produce final output for members of right with no matches for the AllInBoth option
198
+ if ($Type -eq "AllInBoth") {
199
+ for ($i = 0; $i -lt $right.Count; $i++) {
200
+ $rightMatchCount = $rightMatchesCount[$i]
201
+ if ($rightMatchCount -eq 0) {
202
+ $rightItem = $Right[$i]
203
+ WriteJoinObjectOutput $null $rightItem $LeftProperties $RightProperties $Type
204
+ }
205
+ }
206
+ }
207
+ }
208
+ }
209
+ $users = Import-Csv .\user.csv | ForEach-Object { $_.employeeID = "JP" + $_.employeeID; $_ }
210
+ $machines = Import-Csv .\machine.csv
211
+ Join-Object $users $machines -Where { $args[0].employeeID -eq $args[1].Name }`
212
+ -LeftProperties EmployeeName, City, Description -RightProperties Name, operatingSystem |
213
+ Select-Object Name, operatingSystem, EmployeeName, City, Description |
214
+ Export-Csv .\result.csv -NoClobber -NoTypeInformation -Encoding utf8
215
+ ```