【问题标题】:How can I compare two IEnumerable<> objects and return a new one?如何比较两个 IEnumerable<> 对象并返回一个新对象?
【发布时间】:2020-08-03 05:28:15
【问题描述】:

我想比较两个IEnumerable&lt;&gt; 对象并返回一个新的IEnumerable&lt;&gt; 对象。

下面是我的代码,其中我有newFiles 对象,然后我有OriginalFiles 对象。我想比较这两个IEnumerable&lt;&gt; 对象并找到那些是新文件和修改过的文件。

FileConfig 类将每个文件的 md5Hash 值作为字符串,因此我可以比较 md5Hash 字符串上的 OriginalFilesnewFiles 对象以确定哪些文件已更改,然后创建一个新的 IEnumerable&lt;FileConfig&gt;带有那些修改过的文件 + 新文件的对象。

例如: 如果newFiles 对象共有 10 个文件,OriginalFiles 有 8 个文件,则意味着其中有两个新文件。然后剩下的 8 个我将比较并查看使用 md5Hash 字符串更改了哪些文件,因此如果 8 个文件中有 5 个文件更改并且还有两个新文件,那么我将返回 7 个文件作为 IEnumerable&lt;FileConfig&gt; 对象。

public class ProcessFile
{
    public IEnumerable<FileConfig> OriginalFiles { get; set; }


    public IEnumerable<FileConfig> GetNewFiles(IEnumerable<FileConfig> newFiles)
    {
        // compare OriginalFiles and newFiles object and return a new IEnumerable<FileConfig> object 
        // which has only those files which are modified or new by comparing on md5Hash string
       foreach (var element1 in newFiles)
        {
            var newFileName = element1.Name;
            var newMd5Hash = element1.MD5Hash;
            foreach (var element2 in this.OriginalFiles)
            {
                var originalFileName = element2.Name;
                var originalmd5Hash = element2.MD5Hash;
                if (newFileName.Equals(originalFileName, StringComparison.InvariantCultureIgnoreCase) && !newMd5Hash.Equals(originalmd5Hash, StringComparison.InvariantCultureIgnoreCase))
                {
                    yield return new FileConfig
                    {
                        Name = newFileName,
                        Timestamp = element1.Timestamp,
                        MD5Hash = newMd5Hash
                    };
                }
            }
        }

    }
}

public class FileConfig
{
    public string Name { get; set; }
    public DateTime Timestamp { get; set; }
    public string MD5Hash { get; set; }
}

我可以运行两个 for 循环并比较它们的 md5Hash 字符串上的每个文件,并找出哪些文件已被修改并返回新的 IEnumerable&lt;FileConfig&gt; 对象,但是是否有任何快捷方式可以轻松完成相同的事情或其他更好在 C# 中的方式?

【问题讨论】:

  • 您可以使用 Where 和 Select 将代码替换为 Linq 语句。
  • @SveinTerjeGaup 你能提供一个关于我如何使用 linq 做到这一点的例子吗?
  • 你不是在几个小时前问过这个问题吗?
  • 您谈论“简单”和“更好”,但至少对于编程来说,这些并不是一成不变的,而且可能相互矛盾。 Easy 可以尝试创建单个 Linq 语句。这“容易”吗?或者可能是更长的代码,但步骤非常清晰和简单?至于更好的是,您可以使用临时哈希表将时间复杂度从 O(n*n) 降低到 O(n) 来解决这个问题,如果您遇到速度问题,这会更好。在其他情况下,它会过度设计。

标签: c# asp.net linq ienumerable


【解决方案1】:

在我看来,您需要一个左外连接来获取新文件和更改现有文件的情况。应该这样做:

public IEnumerable<FileConfig> GetNewFiles(IEnumerable<FileConfig> newFiles) =>
    from element1 in newFiles
    join element2 in this.OriginalFiles
        on element1.Name.ToLowerInvariant() equals element2.Name.ToLowerInvariant()
        into g
    where !g.Any() || !element1.MD5Hash.Equals(g.First().MD5Hash, StringComparison.InvariantCultureIgnoreCase)
    select new FileConfig
    {
        Name = element1.Name,
        Timestamp = element1.Timestamp,
        MD5Hash = element1.MD5Hash,
    };

如果您将FileConfig 设为只读,那么您可以这样做:

public IEnumerable<FileConfig> GetNewFiles(IEnumerable<FileConfig> newFiles) =>
    from element1 in newFiles
    join element2 in this.OriginalFiles
        on element1.Name.ToLowerInvariant() equals element2.Name.ToLowerInvariant()
        into g
    where !g.Any() || !element1.MD5Hash.Equals(g.First().MD5Hash, StringComparison.InvariantCultureIgnoreCase)
    select element1;

【讨论】:

  • 感谢您的建议。是的,我需要新文件以及修改过的文件。这也行得通 - return newFiles.Where(x =&gt; !OriginalFiles.Any(a =&gt; a.Name == x.Name) || OriginalFiles.Any(a =&gt; a.Name == x.Name &amp;&amp; a.MD5Hash != x.MD5Hash));
  • 另外,如果没有新文件 + 没有修改过的文件,那么我想返回空的 IEnumerable&lt;FileConfig&gt;。这会自动工作吗?
  • @cs98 - 您的查询可以工作,但效率极低。而且,是的,如果没有匹配项,我的答案将返回一个空的可枚举。
  • 啊,我明白了。你能解释为什么它效率低下吗?它会帮助我更好地理解你的建议是否有效?
  • @cs98 - 对于您建议的查询,您为每个 newFiles 元素迭代 OriginalFiles 两次。如果每个列表有 1,000 个元素,那么您就有可能遍历 2,000,000 个元素。通过使用连接,我的查询只会遍历 2,000 个元素。你的可能慢了一千倍。
【解决方案2】:

在你的位置上,我会使用 LinQ。我们也不知道 FileConfig 是什么样的。

此示例返回新文件和更改文件的列表。

我使用过 FileInfo 属性。您的 FileConfig 类可以继承自

public class FileConfig : FileInfo

所以你不会错过那些可比较的属性。

public class ProcessFile
    {
        public IEnumerable<FileInfo> OriginalFiles { get; set; }


        public IEnumerable<FileInfo> GetNewFiles(IEnumerable<FileInfo> newFiles)
        {
            List<FileInfo> result = new List<FileInfo>();
            result.AddRange(newFiles.Where(x => !OriginalFiles.Any(a => a.FullName == x.FullName) || OriginalFiles.Any(a => a.FullName == x.FullName && a.Length != x.Length)));
            return result;

        }
    }

如果您熟悉 LinQ,应该是直截了当的。如果没有,我会建议对它进行一些研究。 https://docs.microsoft.com/cs-cz/dotnet/csharp/tutorials/working-with-linq

如果您有任何问题,我很乐意为您提供帮助。

LinQ 与 FileConfig

public class ProcessFile
    {
        public IEnumerable<FileConfig> OriginalFiles { get; set; }


        public IEnumerable<FileConfig> GetNewFiles(IEnumerable<FileInfo> newFiles)
        {
            List<FileConfig> result = new List<FileConfig>();
            result.AddRange(newFiles.Where(x => !OriginalFiles.Any(a => a.Name == x.Name) || OriginalFiles.Any(a => a.Name == x.Name && a.MD5Hash != x.MD5Hash)));
            return result;

        }
    }

仅返回 IEnumerable:

   public class ProcessFile
        {
            public IEnumerable<FileConfig> OriginalFiles { get; set; }
    
    
            public IEnumerable<FileConfig> GetNewFiles(IEnumerable<FileInfo> newFiles)
            {
                return newFiles.Where(x => OriginalFiles.Any(a => a.Name != x.Name || (a.Name == x.Name && a.MD5Hash != x.MD5Hash)));
    
            }
        }

【讨论】:

  • 感谢您的建议。我添加了 FileConfig 类。这是一个带有 getter 和 setter 的简单类。
  • 我需要比较 md5Hash 字符串来确定哪些文件被修改了。此外,如果OriginalFiles 中不存在新文件,那么我也需要将其返回。 For example: 如果newFiles 对象的总数为10 files 并且OriginalFiles 的总数为8 files,那么这意味着其中有两个新文件。然后剩下8,我将比较并查看使用md5Hash 字符串更改了哪些文件,所以如果8 个文件中有5 个文件更改并且还有两个新文件,那么我将返回7 个文件作为IEnumerable&lt;FileConfig&gt; 对象。
  • @cs98 我添加了 linQ 示例,所以它使用你的类。使用 FileInfo.Legnth 您正在比较位长度,因此如果文件也被修改,也可以使用它。在新示例中,我使用了您的 md5Hash。这两个示例的作用完全相同。
  • 感谢托菲克。现在明白了。所以这将返回新文件+任何更改的文件,对吗?但是任何相同的文件都不会被返回,因为 md5hash 对它们来说是相同的,对吗?我的返回签名也是IEnumerable,但我们要返回列表?我们可以不更改此代码,使其只能返回IEnumerable 吗?
  • 如果所有文件都相同 + 没有新文件存在怎么办?在这种情况下,我想返回空列表或空IEnumerable。也可以这样吗?
【解决方案3】:

您可以使用 LINQ。

public IEnumerable<FileConfig> GetNewFiles(IEnumerable<FileConfig> newFiles)
{
    return
    // compare OriginalFiles and newFiles object and return a new IEnumerable<FileConfig> object 
    // which has only those files which are modified or new by comparing on md5Hash string
    from element1 in newFiles
    let newFileName = element1.Name
    let newMd5Hash = element1.MD5Hash
    from element2 in this.OriginalFiles
    let originalFileName = element2.Name
    let originalmd5Hash = element2.MD5Hash
    where newFileName.Equals(originalFileName, StringComparison.InvariantCultureIgnoreCase) && !newMd5Hash.Equals(originalmd5Hash, StringComparison.InvariantCultureIgnoreCase)
    select new FileConfig
    {
        Name = newFileName,
        Timestamp = element1.Timestamp,
        MD5Hash = newMd5Hash
    };
}

【讨论】:

  • 请阅读 OP 的示例。它显然希望会有新文件。
  • OP 询问问题中提到的代码的快捷方式
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多