如果字符串的子串存在于同一个数组

时间:2018-05-22 20:58:25

标签: arrays powershell foreach contains

Noob在这里。

如果列表中存在父域,我试图通过删除所有子域来削减域列表。我已经成功地拼凑了一个脚本,在经过一些搜索和阅读之后,它会在PowerShell中做到这一点。输出不是我想要的,但可以正常工作。我的解决方案的问题是,由于我的初始列表(数万个条目)的大小,它需要很长时间才能运行。

更新:我已更新我的示例以澄清我的问题。

示例“parent.txt”列表:

adk2.co
adk2.com
adobe.com
helpx.adobe.com
manage.com
list-manage.com
graph.facebook.com

示例输出“repeats.txt”文件:

adk2.com (different top level domain than adk2.co but that's ok)
helpx.adobe.com
list-manage.com (not subdomain of manage.com but that's ok)

然后,我将从父项中删除重复项,并留下“唯一”子域和域列表。我在一个单独的脚本中有这个。

使用我当前脚本的最终列表示例:

adk2.co    
adobe.com
manage.com
graph.facebook.com (it's not facebook.com because facebook.com wasn't in the original list.)

理想的最终名单:

adk2.co
adk2.com (since adk2.co and adk2.com are actually distinct domains)
adobe.com
manage.com
graph.facebook.com

以下是我的代码:

我已经拿走了我的主机列表(parent.txt)并对其自身进行了检查,并将所有匹配项吐出到新文件中。

$parent = Get-Content("parent.txt")
$hosts = Get-Content("parent.txt")
$repeats =@()

$out_file     = "$PSScriptRoot\repeats.txt"

$hosts | where { 
    $found = $FALSE
    foreach($domains in $parent){
        if($_.Contains($domains) -and $_ -ne $domains){
            $found = $TRUE
            $repeats += $_
        }
        if($found -eq $TRUE){
            break
        }
    }
    $found
}

$repeats     = $repeats -join "`n"

[System.IO.File]::WriteAllText($out_file,$repeats)

这似乎是一种非常低效的方法,因为我正在浏览数组的每个元素。关于如何最好地优化这个的任何建议?我有一些想法,比如在检查和检查哪些元素上加入更多条件,但我觉得有一种截然不同的方法会更好。

2 个答案:

答案 0 :(得分:2)

首先,严格基于共享域名的解决方案(例如,helpx.adobe.comadobe.com被视为属于同一个域,但list-manage.commanage.com不是)。 这不是你要求的,但对未来的读者可能更有用:

Get-Content parent.txt | Sort-Object -Unique { ($_ -split '\.')[-2,-1] -join '.' }

假设您的示例输入中有list.manage.com而不是list-manage.com,上面的命令会产生:

adk2.co
adk2.com
adobe.com
graph.facebook.com
manage.com
  • { ($_ -split '\.')[-2,-1] -join '.' }按最后2个域组件(例如adobe.com)对输入行进行排序:

  • -Unique会丢弃重复项。

共享后缀解决方案,根据要求提供:

# Helper function for (naively) reversing a string.
# Note: Does not work properly with Unicode combining characters
#       and surrogate pairs.
function reverse($str) { $a = $str.ToCharArray(); [Array]::Reverse($a); -join $a }

# * Sort the reversed input lines, which effectively groups them by shared suffix
#   with the shortest entry first (e.g., the reverse of 'manage.com' before the
#   reverse of 'list-manage.com').
# * It is then sufficient to output only the first entry in each group, using
#   wildcard matching with -notlike to determine group boundaries.
# * Finally, sort the re-reversed results.
Get-Content parent.txt | ForEach-Object { reverse $_ } | Sort-Object |
  ForEach-Object { $prev = $null } {
    if ($null -eq $prev -or $_ -notlike "$prev*" ) { 
      reverse $_ 
      $prev = $_
    }
  } | Sort-Object

答案 1 :(得分:1)

一种方法是使用哈希表来存储您的所有parent值,然后对于每个repeat,将其从表中删除。添加到哈希表时的值1无关紧要,因为我们只测试密钥是否存在。

$parent = @(
'adk2.co',
'adk2.com',
'adobe.com',
'helpx.adobe.com',
'manage.com',
'list-manage.com'
)

$repeats = (
'adk2.com',
'helpx.adobe.com',
'list-manage.com'
)

$domains = @{}
$parent | % {$domains.Add($_, 1)}
$repeats | % {if ($domains.ContainsKey($_)) {$domains.Remove($_)}}

$domains.Keys | Sort