如果项目包含另一个列表中的字符串,则从一个列表中删除项目
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);
}
}
}
如果问题是由于文件大导致内存使用率高,那么您已经读取了一个文件,但对于另一个文件而不是直接读取内存中的整个文件,您可以使用 FileInputStream 和 BufferedReader 逐行读取行一。这将减少一些内存使用
如果您可以将 a
列表排序为可以支持二进制(或更快)查找的内容,则可以显着 加快速度。
不幸的是,Contains()
搜索使这具有挑战性。但是我们仍然可以做一些事情:
- 避免将所有
b
加载到 RAM 中。曾经.
- 另一方面,如果我们预加载到 RAM 一次,查找
a
会更快,并尽可能多地支持对这个副本的查找尽我们所能。
- 仅将
b
转换为小写一次,而不是对 a
中的每一行再次转换一次。
- 一次完成所有写入操作会更有效率,而不是 re-opening 输出文件在我们找到它们时追加行。
- 作为奖励,我们将用更少的代码完成所有这些工作。
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
中的所有行
我正在寻找从一个列表中删除项目的最有效方法,如果它们包含来自另一个列表的字符串。
例如:
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);
}
}
}
如果问题是由于文件大导致内存使用率高,那么您已经读取了一个文件,但对于另一个文件而不是直接读取内存中的整个文件,您可以使用 FileInputStream 和 BufferedReader 逐行读取行一。这将减少一些内存使用
如果您可以将 a
列表排序为可以支持二进制(或更快)查找的内容,则可以显着 加快速度。
不幸的是,Contains()
搜索使这具有挑战性。但是我们仍然可以做一些事情:
- 避免将所有
b
加载到 RAM 中。曾经. - 另一方面,如果我们预加载到 RAM 一次,查找
a
会更快,并尽可能多地支持对这个副本的查找尽我们所能。 - 仅将
b
转换为小写一次,而不是对a
中的每一行再次转换一次。 - 一次完成所有写入操作会更有效率,而不是 re-opening 输出文件在我们找到它们时追加行。
- 作为奖励,我们将用更少的代码完成所有这些工作。
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
中的所有行