PowerShell - Expand array column in array with repeating rows - powershell

I have an System.Array object called $Data, the first ([0]) item looks like this:
RecordDate : {43739, 43740, 43741, 43742...}
MAX_LAST_UPDATE_DATE : 30/10/2019 14:08:33
EMPLOYEE_NUMBER : 1000522
EFFECTIVE_START_DATE : 01/10/2019 00:00:00
EFFECTIVE_END_DATE : 31/12/4712 00:00:00
CC : 0726
REGION_NAME : Head Office
LOCATION_NAME : Inventory
FIRST_NAME : Name
MIDDLE_NAMES : Mid
LAST_NAME : Last
KNOWN_AS : NickName
JOB_TITLE : Inventory Manager
WORK_NUMBER :
Employment Category : Full Time
NORMAL_HOURS : 40
GROUP_NAME : Indirect
Manager Employee Number : 1034422
PERSON_TYPE : Employee
HIRE_DATE : 16/11/1983 00:00:00
TERMINATION_DATE :
DATE_OF_BIRTH : 23/05/1966 00:00:00
NATIONAL_IDENTIFIER : 111
I'm trying to kind of unpivot the first column "RecordDate" on the entire array like this:
RecordDate : 43739
MAX_LAST_UPDATE_DATE : 30/10/2019 14:08:33
EMPLOYEE_NUMBER : 1000522
EFFECTIVE_START_DATE : 01/10/2019 00:00:00
EFFECTIVE_END_DATE : 31/12/4712 00:00:00
CC : 0726
REGION_NAME : Head Office
LOCATION_NAME : Inventory
FIRST_NAME : Name
MIDDLE_NAMES : Mid
LAST_NAME : Last
KNOWN_AS : NickName
JOB_TITLE : Inventory Manager
WORK_NUMBER :
Employment Category : Full Time
NORMAL_HOURS : 40
GROUP_NAME : Indirect
Manager Employee Number : 1034422
PERSON_TYPE : Employee
HIRE_DATE : 16/11/1983 00:00:00
TERMINATION_DATE :
DATE_OF_BIRTH : 23/05/1966 00:00:00
NATIONAL_IDENTIFIER : 111
RecordDate : 43740
MAX_LAST_UPDATE_DATE : 30/10/2019 14:08:33
EMPLOYEE_NUMBER : 1000522
EFFECTIVE_START_DATE : 01/10/2019 00:00:00
EFFECTIVE_END_DATE : 31/12/4712 00:00:00
CC : 0726
REGION_NAME : Head Office
LOCATION_NAME : Inventory
FIRST_NAME : Name
MIDDLE_NAMES : Mid
LAST_NAME : Last
KNOWN_AS : NickName
JOB_TITLE : Inventory Manager
WORK_NUMBER :
Employment Category : Full Time
NORMAL_HOURS : 40
GROUP_NAME : Indirect
Manager Employee Number : 1034422
PERSON_TYPE : Employee
HIRE_DATE : 16/11/1983 00:00:00
TERMINATION_DATE :
DATE_OF_BIRTH : 23/05/1966 00:00:00
NATIONAL_IDENTIFIER : 111
RecordDate : 43741
MAX_LAST_UPDATE_DATE : 30/10/2019 14:08:33
...
Is there a way to do that with some sneaky Select -expandproperty or do the opposite of what Group-Object is capable of; without doing combination of for($i) and for($j) loops?
It's quite simple on a Table in in Excel PowerQuery, as you just click Expand and voilà.
Regards, Jarek

You can combine Select-Object -ExpandProperty with the common -PipelineVariable parameter and cloning (PSv3+ syntax):
For input collections of [pscustomobject] or [hashtable] instances:
# Sample input array of custom objects to expand by .RecordDate
$array =
[pscustomobject] #{ RecordDate = 1, 2; OtherProp1 = 'one'; OtherProp2 = 'two' },
[pscustomobject] #{ RecordDate = 3, 4; OtherProp1 = 'three'; OtherProp2 = 'four' }
# Write the array elements to the pipeline, and store each in variable
# $objectOrHashtable for use in a later pipeline segment.
Write-Output $array -PipelineVariable objectOrHashtable |
# Expand the input object's .RecordData property, i.e. send its
# elements one by one to the next pipeline segment.
Select-Object -ExpandProperty RecordDate |
ForEach-Object {
# Clone the original input object.
$clone = if ($objectOrHashtable -is [Management.Automation.PSCustomObject]) {
$objectOrHashtable.psobject.Copy()
} else { # assume [hashtable] or a type that implements [System.ICloneable]
$objectOrHashtable.Clone()
}
# Assign the record date at hand to the clone...
$clone.RecordDate = $_
# ... and output it.
$clone
}
The above yields the following; note that 4 objects were output, based on enumerating the elements of the input objects' .RecordDate array while retaining all other properties:
RecordDate OtherProp1 OtherProp2
---------- ---------- ----------
1 one two
2 one two
3 three four
4 three four
Note:
The above works with two types of input object:
custom objects ([pscustomobject] instances, such as created by Import-Csv)
Note: For technical reasons you cannot use -is [pscustomobject] and must instead use the full type name, System.Management.Automation.PSCustomObject (the System. prefix can be omitted); [pscustomobject], for historical reasons, is the same as [psobject] (System.Management.Automation.PSObject), and -is [psobject] is also true for objects that aren't custom objects.
hashtables (System.Collections.Hashtable instances - but not [ordered] hashtables); more generally, any type that implements System.ICloneable.
The cloning that is performed on custom objects and hashtable is shallow (member-wise), but with scalar string and numeric values that is sufficient.
Generally, the ICloneable interface doesn't prescribe the specifics of the cloning behavior, which is why its use is generally discouraged.
For input collections of [System.Data.DataRow] instances:
Cloning a collection of System.Data.DataRow instances - the rows of data table, System.Data.DataTable - requires custom cloning logic, but the approach and structure of the output are fundamentally the same:
# Create a sample DataTable...
$dt = [System.Data.DataTable]::new('sample')
# ... define the columns ...
$dt.Columns.AddRange([System.Data.DataColumn[]] (
#{ ColumnName = 'RecordDate'; DataType = [object[]] },
#{ ColumnName = 'OtherProp1'; DataType = [string] },
#{ ColumnName = 'OtherProp2'; DataType = [string] }
))
# ...and add sample rows.
#{ RecordDate = 1, 2; OtherProp1 = 'one'; OtherProp2 = 'two' },
#{ RecordDate = 3, 4; OtherProp1 = 'three'; OtherProp2 = 'four' } | % {
$dt.Rows.Add(($dr = $dt.NewRow()))
foreach ($entry in $_.GetEnumerator()) {
$dr[$entry.Key] = $entry.Value
}
}
# Create an auxiliary, empty clone of the input data table
# to facilitate cloning of individual rows.
$dtAux = $dt.Clone()
# Write the data rows to the pipeline, and store each in variable
# $obj for use in a later pipeline segment.
Write-Output $dt.Rows -PipelineVariable row |
# Expand the input object's .RecordData property, i.e. send its
# elements one by one to the next pipeline segment.
Select-Object -ExpandProperty RecordDate |
ForEach-Object {
# Clone the data row at hand.
$dtAux.Clear(); $dtAux.ImportRow($row)
$clone = $dtAux.Rows[0]
# Assign the record date at hand to the clone...
$clone.RecordDate = #($_)
# ... and output it.
$clone
}

Related

i want read date from the file name and keep last 5 dates files using powershell

i have set of files in a specific folder,
i want read date from the file name,
select last 2 dates files and move all other dates files to another location using powershell
below is the file name sample i have
Directory: E:\HOLDS\trim
Name
----
17988000412767900-20170402-T
17988000412770804-20170402-T
17988000412773204-20170402-T
17988000412792005-20170402-T
17988000412794300-20170402-T
17991325988242500-20170403-C
17991325988242800-20170403-C
17991325988243000-20170403-C
17991325988245000-20170403-C
17991325988245200-20170403-C
17992327574130910-20170404-T
17992327574131100-20170404-T
17992327574145005-20170404-T
17992327574145209-20170404-T
17992327574169106-20170404-T
17993057054385600-20170405-T
17993326857390200-20170405-R
17993327575638604-20170405-T
17993327575676304-20170405-T
17993327575835705-20170405-T
17993327575844703-20170405-T
17997018695202606-20170409-T
17998001450000100-20170409-C
17998001450001000-20170409-C
17998057920002100-20170409-R
17998119423714112-20170410-T
17998119423728401-20170410-T
17998282230003400-20170409-R
17998297810002500-20170409-R
17998327575543207-20170410-T
17998327575543708-20170410-T
17998327575546104-20170410-T
17998327575547600-20170410-T
17998327575591805-20170410-T
is there any reason why you can't filter by the last write time instead?
$sourcedir = "c:\scripts\"
$files = get-childitem $sourcedir | sort LastwriteTime
# last files
$files[-1].name
# second from last file
$files[-2].Name
From your requirement, if your file name is always follow same style, I think you can do this with this flow:
1. Define an Direcroty/Map which is an
2. Get all the files, then loop the files & split the file name with '-' & take the second as date, then add the into the directory/map
3. Move all files which not satisfied condition out
$maps = New-Object 'system.collections.generic.dictionary[[string],[system.collections.generic.list[string]]]';
$list = New-Object 'system.collections.generic.list[string]';
$files = ("17988000412767900-20170402-T",
"17988000412770804-20170402-T",
"17988000412773204-20170402-T",
"17988000412792005-20170402-T",
"17988000412794300-20170402-T",
"17991325988242500-20170403-C",
"17991325988242800-20170403-C",
"17991325988243000-20170403-C",
"17991325988245000-20170403-C",
"17991325988245200-20170403-C",
"17992327574130910-20170404-T",
"17992327574131100-20170404-T",
"17992327574145005-20170404-T",
"17992327574145209-20170404-T",
"17992327574169106-20170404-T",
"17993057054385600-20170405-T",
"17993326857390200-20170405-R",
"17993327575638604-20170405-T",
"17993327575676304-20170405-T",
"17993327575835705-20170405-T",
"17993327575844703-20170405-T",
"17997018695202606-20170409-T",
"17998001450000100-20170409-C",
"17998001450001000-20170409-C",
"17998057920002100-20170409-R",
"17998119423714112-20170410-T",
"17998119423728401-20170410-T",
"17998282230003400-20170409-R",
"17998297810002500-20170409-R",
"17998327575543207-20170410-T",
"17998327575543708-20170410-T",
"17998327575546104-20170410-T",
"17998327575547600-20170410-T",
"17998327575591805-20170410-T");
$files | %{
$date = $_.Split('-')[1];
if($maps.Keys.Contains($date)){
$maps[$date].Add($_);
}
else{
$maps.Add($date, $list);
$maps[$date].Add($_);
}
}
The output will be:
Key : 20170402
Value : {17988000412767900-20170402-T, 17988000412770804-20170402-T, 17988000412773204-20170402-T, 17988000412792005-20170402-T...}
Key : 20170403
Value : {17988000412767900-20170402-T, 17988000412770804-20170402-T, 17988000412773204-20170402-T, 17988000412792005-20170402-T...}
Key : 20170404
Value : {17988000412767900-20170402-T, 17988000412770804-20170402-T, 17988000412773204-20170402-T, 17988000412792005-20170402-T...}
Key : 20170405
Value : {17988000412767900-20170402-T, 17988000412770804-20170402-T, 17988000412773204-20170402-T, 17988000412792005-20170402-T...}
Key : 20170409
Value : {17988000412767900-20170402-T, 17988000412770804-20170402-T, 17988000412773204-20170402-T, 17988000412792005-20170402-T...}
Key : 20170410
Value : {17988000412767900-20170402-T, 17988000412770804-20170402-T, 17988000412773204-20170402-T, 17988000412792005-20170402-T...}
Then you can order the $map and loop all the files in it's value & move them out.
try this
$yourdirpath="C:\Temp\yourfolder"
$yourmovedir="C:\Temp\movedir"
$numrow=0
$currentdate=""
Get-ChildItem $yourdirpath |
select FullName, #{N="DateFile";E={$_.Name.substring(18, 8)}} |
sort DateFile -Descending |
foreach{
if ($_.DateFile -ne $currentdate)
{
$numrow++
$currentdate=$_.DateFile
}
[pscustomobject]#{FullName=$_.FullName;DateFile=$_.DateFile;numrow=$numrow}
} | where numrow -gt 2 | foreach{move-item $_.FullName -Destination $yourmovedir }

Indexing into a PowerShell value collection

Given:
$settings = #{"Env1" = "VarValue1"; "Env2" = "VarValue2" }
Write-Output "Count: $($settings.Values.Count)"
Write-Output "Value 0: '$($settings.Values[0])'"
Write-Output "Value 1: '$($settings.Values[1])'"
I get the output:
Count: 2
Value 0 : 'VarValue2 VarValue1'
Value 1 : ''
Why does the first element have both values and the second have none? How do I get the values as a collection I can index?
I figured it out. The solution is to convert the Values ICollection to an array of strings.
With:
$values = $settings.Values -as [string[]]
The output becomes as originally expected:
Count: 2
Value 0 : 'VarValue2'
Value 1 : 'VarValue1'
I cannot help but feel that this should be the default behaviour.
You can't directly index a hashtable like that. Gotta do this
($settings.GetEnumerator() | select -expand value)[0]
From help about_hash_tables
Hash table tables are not arrays, so you cannot use an integer as
an
index into the hash table, but you can use a key name to index into the
hash table. If the key is a string value, enclose the key name in quotation
marks.
Based on your $settings object, you can simply use » ($settings).Values
Example:
($settings).Values | % { '• ' + $_ }
Returns:
• VarValue2
• VarValue1

OrientDB SQL - traverse while keeping edges weight

Lets assume the following model in OrientDB graph:
I have a Profile vertex.
Profiles are connected with 2 edges: Liked and Commented. Both edges have a "value" field indicating the count of the action (or the "weight" of the edge).
So, if user A commented 3 times on posts by user B there will be a Commented edge from user A to user B with value = 3.
Now, say I want to get all the users that interacted with user B (either liked or commented), sorted by the weight of the interaction.
I can do that with the following SQL:
select * from (traverse out from
(select out, sum(value) as value from
(traverse * from (select from Profile where username="B") while $depth < 3)
where #class="Liked" or #class="Commented" group by out order by value desc)
while $depth < 2 ) where #class="Profile" )
But what if I want to know also the weight of the interaction? How do I propagate up the "value" while doing the last traverse?
Edit
According to the suggestion, a simplified version of this query will be:
select expand(out) from (
select out, sum(value) as value from (
select expand(inE("Liked", "Commented")) from Profile
where username="B"
) group by out order by value desc
)
But I still can't find a way to use LET to insert the value into the outer expanded object. $parent does not seem to point to the object that is expanded on the most outer select.
Edit 2
I'm Playing with $parent in every way I can think of. I don't see how you can use it in this case. Again - the problem I'm trying to solve is how to pass the sum(value) to the outer result set. I don't see a way of using LET for it when doing a GROUP BY, and I also don't see a way of using LET when the outer most select is doing an expand (since you can't do other projections together with expand).
Also, the results of using $current do not seem to be what is expected. For example, the following query:
select expand($v) from
(select from
(select expand(inE("Liked", "Commented")) from Profile where #rid=#11:0)
let $v = $current
)
Returns this:
{
"result" : [{
"#type" : "d",
"#rid" : "#14:4",
"#version" : 2,
"#class" : "Commented",
"value" : 1,
"out" : "#11:165",
"in" : "#11:0"
}, {
"#type" : "d",
"#rid" : "#14:4",
"#version" : 2,
"#class" : "Commented",
"value" : 1,
"out" : "#11:165",
"in" : "#11:0"
}, {
"#type" : "d",
"#rid" : "#14:4",
"#version" : 2,
"#class" : "Commented",
"value" : 1,
"out" : "#11:165",
"in" : "#11:0"
}
]
}
The same node over and over again, instead of all the edges, which is what I would expect.
I see you're using an old version of OrientDB. With more recent versions you can simplify it by. Example: original query:
select * from (
traverse out from (
select out, sum(value) as value from (
traverse * from (
select from Profile where username="B"
) while $depth < 3
) where #class="Liked" or #class="Commented" group by out order by value desc
) while $depth < 2
) where #class="Profile" )
You could skip some step by using out()/in()/both() passing the Edge's labels/class like:
select expand( out(["Liked","Commented]) ) from Profile where username="B"
However to pass the value you can use variables with LET clause. Example:
select from XXX let $parent.a = value
In this way you set the variable "a" into the upper level context, but you could do also:
select from XXX let $parent.$parent.a = value
To set it 2 levels up.
I haven't tried this with a group by yet, but you should be able to group the result using a sub query. This works for me, where prop1, prop2, prop3, etc are properties of the vertices coming out of the edge (the columns resulting from select out() Profile where username="B")
select outV().prop1, outV().prop2, outV().prop3, value from (
select expand(inE("Liked", "Commented")) from Profile
where username="B"
) order by value desc

Postgres query issues

I'm not sure why this structure is not working. This is my first time working with postgres and was hoping someone could help me.
SELECT * FROM "friends" WHERE "from" = '1' OR "to" = '1' AND "status" = '1'
It returns all values where from where "from" is = 1 and "to" = 1 rather than one or the other where "status" is = 1
I hope that isn't too confusing.
Thanks.
OR operator has lower precedence than AND [1]. As a result, the expression is evaluated as follows:
(
"from" = '1'
)
OR
(
"to" = '1'
AND
"status" = '1'
)
What you probably want instead is:
SELECT * FROM "friends" WHERE ("from" = '1' OR "to" = '1') AND "status" = '1'

Selecting a set of three distinct objects

Drools Planner used this to select two distinct objects, ensuring that a rule did not fire twice for a given pair of objects. If o1 and o2 made a match, it fired only once, not twice for (o1,o2) and (o2,01).
when
$stp1 : SubjectTeacherPeriod( $slno : slNo,
$id : id,
$period_day_order : period.dayOrder
)
$stp2 : SubjectTeacherPeriod( slNo > $slno,
id == $id,
period.dayOrder == $period_day_order
)
How do I select a set of three distinct objects? What is a good selection criteria for that?
Same approach should work:
$f1 : SomeFact( ... )
$f2 : SomeFact( id > $f1.id, ... )
$f3 : SomeFact( id > $f2.id, ... )