繁体   English   中英

如果项目包含来自另一个列表的字符串,则从一个列表中删除它们

[英]Remove items from one list if they contain strings from another list

如果项目包含另一个列表中的字符串,我正在寻找从一个列表中删除项目的最有效方法。

例如:

B 列表包含:

TomWentFishing
SueStayedHome
JohnGoesToSchool
JimPlaysTennis

一个列表包含:

GoesToSchool
SueStayed

C 列表应包含:

TomWentFishing
JimPlaysTennis

我已经使用了这段代码,但是由于列表非常大,它会占用很多时间:

static void Main(string[] args)
    {
        string[] b = File.ReadAllLines(@"C:\b.txt");
        string[] a = File.ReadAllLines(@"C:\a.txt");

        foreach (string firststring in b)
        {
            bool contains = false;
            foreach (string secondstring in a)
            {
                if (firststring.ToLower().Contains(secondstring.ToLower()))
                {
                    contains = true;
                    break;
                }
            }

            if (contains == false)
            {
                File.AppendAllText(@"C:\c.txt", firststring + Environment.NewLine);
            }


        }

    }

如果您可以将列表排序a可以支持二进制(或更快)查找的内容,则可以显着加快速度。

不幸的是, Contains()搜索使这具有挑战性。 但是我们仍然可以做一些事情:

  • 避免将所有b加载到 RAM 中。 曾经。
  • 另一方面,如果我们一次预加载到 RAM 中,查找到a会更快,并尽可能多地支持对这个副本的查找。
  • 仅将b转换为小写一次,而不是对a中的每一行再次转换。
  • 一次完成所有的写操作会更有效率,而不是重新打开输出文件来追加我们找到的行。
  • 作为奖励,我们将以更少的代码完成所有这些工作。
static void Main(string[] args)
{
    var b = File.ReadLines(@"C:\b.txt");
    var a = File.ReadLines(@"C:\a.txt").Select(line => line.ToLower()).ToList();

    var result = b.Where(bline => {
       var lowered = bline.ToLower();
       return !a.Any(aline => lowered.Contains(aline));
    });

    File.AppendAllLines(@"C:\c.txt", result);
}

在这里,您有一个非常有效的基于哈希集的实现,它是线性时间复杂度 O(n)。 这避免了您为 b.txt 文件中的每一行迭代 a.txt 文件的所有行,这会导致二次时间复杂度 O(n^2)。

如果包含所有 a.txt 文件行的哈希集适合内存,则此方法很好。 如果它不适合内存,那么您需要使用 RocksDb 之类的东西。

首先你有这个扩展方法:

public static class EnumerableStringExtensions
{
    public static IEnumerable<string> Minus(
        this IEnumerable<string> minuend, 
        IEnumerable<string> subtrahend, 
        StringComparison comparisonType)
    {
        var subtrahendSet = new HashSet<string>(subtrahend, StringComparer.FromComparison(comparisonType));
        return minuend.Where(x => subtrahendSet.Contains(x) == false);
    }
}

你可以像这样使用它:

public class Program
{
    public static IEnumerable<string> EnumerateLines(string filePath)
    {
        using (var reader = File.OpenText(filePath))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                yield return line;
            }
        }
    }

    static void Main(string[] args)
    {
        var minuend = EnumerateLines("b.txt");
        var sustraend = EnumerateLines("a.txt");
        var difference = minuend.Minus(sustraend, StringComparison.OrdinalIgnoreCase);
        File.WriteAllLines("difference.txt", difference);

    }
}

请注意,使用此实现,您不需要一次将 b.txt 文件中的所有行保存在内存中。 但是您需要一个包含 a.txt 中所有行的哈希集

如果问题是由于文件大小而导致内存使用率高,那么您已经读取了一个文件,但对于另一个文件而不是直接读取内存中的整个文件,您可以使用 FileInputStream 和 BufferedReader 逐行读取。 这将减少一些内存使用

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM