[英]Powershell: How to reduce my comparison time of two Arrays
我目前对PowerShell和Programming总体而言还很陌生。 我正在开发一个在PowerShell中使用的工具,该工具可以接收两个CSV文件,这些文件的范围从5,000-40,000行到每行30+个标题。 该工具获取这两个文件,并根据键(符号)查找匹配的行,并报告相应字段中的差异。
该脚本有两个参数:两个要比较的CSV文件。 在下面以gcFile1和gcFile2表示
这些CSV文件的行数或符号都不会相同,而是按字母顺序列出。
我能够根据所需的键匹配字符串,执行比较并正确输出差异。
我的问题是,这花费了太长的时间,我的假设是,因为我使用两个ForEach循环来比较对象,所以它基本上对每一行都执行此操作,因此花费的时间比允许的长得多。
我正在寻找一种方法,以便在每次使用较小的数组进行搜索时,将其从后续搜索中删除。
非常感谢您的帮助:)
这是包含ForEach循环的代码段:
#For each line in the first file
ForEach($line1 in $gcFile1)
{
#For Each line in the second file
ForEach($line2 in $gcFile2)
{
#If the symbol from file one is like a symbol from file 2
If($line1.Split(';').Get(0) -like $line2.Split(';').Get(0))
{
$Symbol1 = $line1.Split(';').Get(0)
$Symbol2 = $line2.Split(';').Get(0)
for($x=0;$x -lt $headerCount1; $x++)
{
If($line1.Split(';').Get($x) -like $line2.Split(';').Get($x))
{
$Version1 = $line1.Split(';').Get($x)
$Version2 = $line2.Split(';').Get($x)
} else {
$Version1 = $line1.Split(';').Get($x)
$Version2 = $line2.Split(';').Get($x)
$headerName1 = $headerArray1[$x]
$headerName2 = $headerArray2[$x]
$bufferLength = 30 - $headerName1.Length
$pad = " "
for($y = 0;$y -lt $bufferLength; $y++){
$pad += " "
}
Write-Host "[$headerName1]$pad[$Version1 / $Version2]"
Add-Content $logfileBoth "[$headerName1]$pad[$Version1 / $Version2]"
}
}
}
}
}
CSV中的示例:
Symbol;Validity;AnnualHighDate-Date;AnnualHighDate-Time;AnnualLowDate-Date;AnnualLowDate- Time;AverageVolume100Day;AverageVolume22Day;Beta;ClosePriceMonth;ClosePriceQuarter;ClosePriceWeek;Clo sePriceYear;HighPriceCalendar;LowPriceCalendar;Mo12RateOfReturn;MovingAverage100Day;MovingAverage14Day;MovingAverage200Day;MovingAverage21Day;MovingAverage50Day;MovingAverage9Day;Volatility20Day;Volatility6Month;YTDRateOfReturn;AverageVolume250;HighDateCalendar;Size;AnnualHighDate;AnnualLowDate;CalcLastUpdate
A;valid;20140122;0;20130904;0;1.81273e+006;1.85068e+006;1.3787;57.16;57.44;57.16;57.19;61.22;51.96;0.2481;56.54;57.68;56.59;56.81;56.92;57.67;0.1804;0.1796;0.0198;2320468;20140122;248;1/22/2014;9/4/2013;9/3/2014
AA;valid;20140723;0;20130904;0;1.52891e+007;1.1017e+007;1.5202;16.61;14.89;16.61;10.63;17.22;9.82;1.2085;14.92;16.49;13.02;16.4;16.11;16.59;0.146;0.2494;0.6011;22428276;20140723;248;7/23/2014;9/4/2013;9/3/2014
例如,我将在文件1中找到符号A,在文件2中搜索符号A,然后比较与相同标题对应的列。
期望的结果是输出带有Symbol和版本1和版本2的不同列的列表
样本输出:
============================== A ==============================
[Header] [file1.txt / file2.txt]
[AverageVolume100Day] [1.84354e+006 / 1.81273e+006]
[AverageVolume22Day] [1.85629e+006 / 1.85068e+006]
[Beta] [1.5311 / 1.3787]
[Mo12RateOfReturn] [0.2484 / 0.2481]
[MovingAverage100Day] [56.4635 / 56.54]
[MovingAverage14Day] [57.455 / 57.68]
[MovingAverage200Day] [56.5412 / 56.59]
[MovingAverage21Day] [56.7281 / 56.81]
[MovingAverage50Day] [56.9214 / 56.92]
[MovingAverage9Day] [57.7011 / 57.67]
[Volatility20Day] [0.0508 / 0.1804]
[Volatility6Month] [0.1285 / 0.1796]
[YTDRateOfReturn] [0.02 / 0.0198]
[AverageVolume250] [2325140 / 2320468]
============================== AA ==============================
[Header] [file1.txt / file2.txt]
[AverageVolume100Day] [1.58983e+007 / 1.52891e+007]
[AverageVolume22Day] [1.11858e+007 / 1.1017e+007]
[Beta] [1.6706 / 1.5202]
[LowPriceCalendar] [9.825 / 9.82]
[Mo12RateOfReturn] [1.1749 / 1.2085]
[MovingAverage100Day] [14.8568 / 14.92]
[MovingAverage14Day] [16.4471 / 16.49]
[MovingAverage200Day] [12.9426 / 13.02]
[MovingAverage21Day] [16.3967 / 16.4]
[MovingAverage50Day] [16.0764 / 16.11]
[MovingAverage9Day] [16.5478 / 16.59]
[Volatility20Day] [0.0385 / 0.146]
[Volatility6Month] [0.178 / 0.2494]
[YTDRateOfReturn] [0.5767 / 0.6011]
[AverageVolume250] [22544029 / 22428276]
此类问题的规范答案是使用查找表。 有多种创建方法。 通用方法如下。
从第一个输入为每个数据行计算一个哈希。 将哈希存储在容器中。 准备好查找表后,逐行读取第二个文件并以相同方式计算哈希。 检查查找表是否包含哈希。 如果没有,那么您将获得第一个文件中不存在的行。 如果是这样,则您完全匹配。
可以使用例如MD5计算哈希值。 将哈希存储在排序列表中,并使用二进制搜索在O(n log n)中查找匹配项。 更简单的选择是使用哈希表(aka关联数组),该哈希表在幕后进行哈希计算。
在您的情况下,整个数据行的散列似乎不可行。 生成仅使用哈希来查找相关行以进行进一步处理的查找表可能更合适。
至于如何创建查找表,请看另一篇文章 。
您至少需要PowerShell 3.0才能运行。 虽然可以更改为支持2.0
$firstData = Import-CSV C:\temp\sample.csv -Delimiter ";" | Group-Object -AsHashTable -AsString -Property Symbol
$secondData = Import-CSV C:\temp\sample2.csv -Delimiter ";" | Group-Object -AsHashTable -AsString -Property Symbol
$firstData.GetEnumerator() | ForEach-Object{
If ($secondData.ContainsKey($_.Key)){
$symbol = $_.Key
[PSCustomObject]@{
'Symbol' = $symbol
'AverageVolume100Day' = "$($firstData[$symbol].AverageVolume100Day) / $($secondData[$symbol].AverageVolume100Day)"
'AverageVolume22Day' = "$($firstData[$symbol].AverageVolume22Day) / $($secondData[$symbol].AverageVolume22Day)"
}
}
}
该解决方案不是完全构建的,但是足以向您展示我正在尝试做的事情。 您可以这样做,以便将所有您感兴趣的参数分开存储,而不是像我对AverageVolume100Day
和AverageVolume22Day
所做的那样将所有内容拼写出来。 另外,我没有足够的样本数据来进行此操作
这样做是将两个数据样本都导入为CSV
并将其转换为哈希表,其中Name
是符号,其余数据是值。
循环遍历每个符号并验证其是否在其他样本数据集中匹配。 如果找到匹配项,则构建一个自定义对象,该对象具有每个数据样本集中的每个值,并与数据中的反斜杠进行比较。
我从输出中省略了Header,因为重复进行似乎是重复的:)。 我不知道这是否会更有效,但我会考虑尝试一下。
样本输出。
Symbol AverageVolume100Day AverageVolume22Day
------ ------------------- ------------------
AA 1.52891e+007 / 1.52891e+007 1.1017e+007 / 1.1017e+007
A 1.81273e+006 / 1.81573e+006 1.85068e+006 / 1.85368e...
Powershell中的输出可能不那么可读,并且某些列可能已被删除。 将这些全部发送到Export-CSV
将是一种选择。
来自评论的更新
这是一种类似的解决方案,具有动态标头的额外好处。 我需要对输出进行一些处理,因为我对此还不满意。
$firstData = Import-CSV C:\temp\sample.csv -Delimiter ";" | Group-Object -AsHashTable -AsString -Property Symbol
$secondData = Import-CSV C:\temp\sample2.csv -Delimiter ";" | Group-Object -AsHashTable -AsString -Property Symbol
$propertyNames = @("AverageVolume100Day","AverageVolume22Day","AnnualHighDate-Date")
$properties = @{}
$firstData.GetEnumerator() | ForEach-Object{
If ($secondData.ContainsKey($_.Key)){
$symbol = $_.Key
$properties.Symbol = $symbol
ForEach($property in $propertyNames) {
$properties.$property = "$($firstData[$symbol].$property) / $($secondData[$symbol].$property)"
}
New-Object Psobject -Property $properties
}
} | Format-List
根据需要使用数组$propertyNames
填写标题。 在每个循环的ForEach-Loop
并构建$properties
。 当您有许多标头时, Format-List
将使输出可读。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.