C# 如何使用被动扩展名检测大文件中唯一的行

C# 如何使用被动扩展名检测大文件中唯一的行,c#,system.reactive,yield,file-processing,C#,System.reactive,Yield,File Processing,我必须处理大型CSV文件(高达数十GB),如下所示: Key,CompletedA,CompletedB 1,true,NULL 2,true,NULL 3,false,NULL 1,NULL,true 2,NULL,true Task<TResult> SomeRxTransformation1(IObservable<Record> source); Task<Record[][]> task1 = GroupByKeyExcludingSomeG

我必须处理大型CSV文件(高达数十GB),如下所示:

Key,CompletedA,CompletedB
1,true,NULL
2,true,NULL
3,false,NULL
1,NULL,true
2,NULL,true  
Task<TResult> SomeRxTransformation1(IObservable<Record> source);
Task<Record[][]> task1 = GroupByKeyExcludingSomeGroups(subject);
source.ToObservable().Subscribe(subject); // This line does all the work
Record[][] result1 = task1.Result;
我有一个解析器,它将解析后的行生成为
IEnumerable
,这样我一次只读取内存中的一行

现在我必须按键对记录进行分组,并检查CompletedA和CompletedB列在组中是否有值。在输出上,我需要记录,该记录在组中没有CompletedA和CompletedB

在这种情况下,使用键3进行记录

然而,在同一个数据集上有许多类似的处理,我不想重复多次

我认为我可以将IEnumerable转换为IObservable,并使用反应式扩展来查找记录


是否可以在IObservable集合上使用简单的Linq表达式在内存中高效地执行此操作?

如果
是一个整数,我们可以尝试使用
字典
和一次扫描:

 // value: 0b00 - neither A nor B
 //        0b01 - A only
 //        0b10 - B only
 //        0b11 - Both A and B    
 Dictionary<int, byte> Status = new Dictionary<int, byte>();

 var query = File
   .ReadLines(@"c:\MyFile.csv")
   .Where(line => !string.IsNullOrWhiteSpace(line))
   .Skip(1) // skip header 
   .Select(line => YourParserHere(line));

 foreach (var record in query) {
   int mask = (record.CompletedA != null ? 1 : 0) |
              (record.CompletedB != null ? 2 : 0); 

   if (Status.TryGetValue(record.Key, out var value))
     Status[record.Key] = (byte) (value | mask);
   else
     Status.Add(record.Key, (byte) mask);
 }

 // All keys that don't have 3 == 0b11 value (both A and B)  
 var bothAandB = Status
   .Where(pair => pair.Value != 3)
   .Select(pair => pair.Key); 
//值:0b00-既不是A也不是B
//0b01-仅限A
//仅限0b10-B
//0b11-A和B都是
字典状态=新建字典();
var query=File
.ReadLines(@“c:\MyFile.csv”)
.Where(line=>!string.IsNullOrWhiteSpace(line))
.Skip(1)//跳过标题
.选择(行=>YourParsere(行));
foreach(查询中的var记录){
int mask=(record.CompletedA!=null?1:0)|
(record.CompletedB!=null?2:0);
if(Status.TryGetValue(record.Key,out var value))
状态[记录.键]=(字节)(值|掩码);
其他的
添加状态(record.Key,(字节)掩码);
}
//所有没有3==0b11值的键(A和B)
var bothAandB=状态
.Where(pair=>pair.Value!=3)
.Select(pair=>pair.Key);

如果
是一个整数,我们可以尝试使用
字典和一次扫描:

 // value: 0b00 - neither A nor B
 //        0b01 - A only
 //        0b10 - B only
 //        0b11 - Both A and B    
 Dictionary<int, byte> Status = new Dictionary<int, byte>();

 var query = File
   .ReadLines(@"c:\MyFile.csv")
   .Where(line => !string.IsNullOrWhiteSpace(line))
   .Skip(1) // skip header 
   .Select(line => YourParserHere(line));

 foreach (var record in query) {
   int mask = (record.CompletedA != null ? 1 : 0) |
              (record.CompletedB != null ? 2 : 0); 

   if (Status.TryGetValue(record.Key, out var value))
     Status[record.Key] = (byte) (value | mask);
   else
     Status.Add(record.Key, (byte) mask);
 }

 // All keys that don't have 3 == 0b11 value (both A and B)  
 var bothAandB = Status
   .Where(pair => pair.Value != 3)
   .Select(pair => pair.Key); 
//值:0b00-既不是A也不是B
//0b01-仅限A
//仅限0b10-B
//0b11-A和B都是
字典状态=新建字典();
var query=File
.ReadLines(@“c:\MyFile.csv”)
.Where(line=>!string.IsNullOrWhiteSpace(line))
.Skip(1)//跳过标题
.选择(行=>YourParsere(行));
foreach(查询中的var记录){
int mask=(record.CompletedA!=null?1:0)|
(record.CompletedB!=null?2:0);
if(Status.TryGetValue(record.Key,out var value))
状态[记录.键]=(字节)(值|掩码);
其他的
添加状态(record.Key,(字节)掩码);
}
//所有没有3==0b11值的键(A和B)
var bothAandB=状态
.Where(pair=>pair.Value!=3)
.Select(pair=>pair.Key);

我认为这将满足您的需要:

var result =
    source
        .GroupBy(x => x.Key)
        .SelectMany(xs =>
            (xs.Select(x => x.CompletedA).Any(x => x != null && x == true) && xs.Select(x => x.CompletedA).Any(x => x != null && x == true))
            ? new List<Record>()
            : xs.ToList());
var结果=
来源
.GroupBy(x=>x.Key)
.SelectMany(xs=>
(x=>x.CompletedA.Any(x=>x!=null&&x==true)和&xs.Select(x=>x.CompletedA.Any(x=>x!=null&&x==true))
?新名单()
:xs.ToList());

使用Rx在这里没有帮助。

我认为这将满足您的需要:

var result =
    source
        .GroupBy(x => x.Key)
        .SelectMany(xs =>
            (xs.Select(x => x.CompletedA).Any(x => x != null && x == true) && xs.Select(x => x.CompletedA).Any(x => x != null && x == true))
            ? new List<Record>()
            : xs.ToList());
var结果=
来源
.GroupBy(x=>x.Key)
.SelectMany(xs=>
(x=>x.CompletedA.Any(x=>x!=null&&x==true)和&xs.Select(x=>x.CompletedA.Any(x=>x!=null&&x==true))
?新名单()
:xs.ToList());

使用Rx在这里没有帮助。

是的,Rx库非常适合这种同步枚举一次/计算多次操作。您可以使用
主题
作为一对多传播器,然后您应该将各种Rx操作符附加到它,然后您应该向它提供源枚举表中的记录,最后您将从附加的操作符收集结果,这些操作符现在将完成。以下是基本模式:

IEnumerable<Record> source = GetRecords();
var subject = new Subject<Record>();
var task1 = SomeRxTransformation1(subject);
var task2 = SomeRxTransformation2(subject);
var task3 = SomeRxTransformation3(subject);
source.ToObservable().Subscribe(subject); // This line does all the work
var result1 = task1.Result;
var result2 = task2.Result;
var result3 = task3.Result;
例如,要进行的特殊分组需要进行如下转换:

Task<Record[][]> GroupByKeyExcludingSomeGroups(IObservable<Record> source)
{
    return source
        .GroupBy(record => record.Key)
        .Select(grouped => grouped.ToArray())
        .Merge()
        .Where(array => array.All(r => !r.CompletedA && !r.CompletedB))
        .ToArray()
        .ToTask();
}
TaskGroupByKeyExcludingsomeGroups(IObservable源)
{
返回源
.GroupBy(record=>record.Key)
.Select(grouped=>grouped.ToArray())
.Merge()
.Where(array=>array.All(r=>!r.CompletedA&&!r.CompletedB))
.ToArray()
.ToTask();
}
当您将其合并到模式中时,它将如下所示:

Key,CompletedA,CompletedB
1,true,NULL
2,true,NULL
3,false,NULL
1,NULL,true
2,NULL,true  
Task<TResult> SomeRxTransformation1(IObservable<Record> source);
Task<Record[][]> task1 = GroupByKeyExcludingSomeGroups(subject);
source.ToObservable().Subscribe(subject); // This line does all the work
Record[][] result1 = task1.Result;
Task task1=groupbykey排除某些组(主题);
source.ToObservable().Subscribe(subject);//这条线做所有的工作
记录[][]result1=task1.Result;

是的,Rx库非常适合这种同步枚举一次/计算多次操作。您可以使用
主题
作为一对多传播器,然后您应该将各种Rx操作符附加到它,然后您应该向它提供源枚举表中的记录,最后您将从附加的操作符收集结果,这些操作符现在将完成。以下是基本模式:

IEnumerable<Record> source = GetRecords();
var subject = new Subject<Record>();
var task1 = SomeRxTransformation1(subject);
var task2 = SomeRxTransformation2(subject);
var task3 = SomeRxTransformation3(subject);
source.ToObservable().Subscribe(subject); // This line does all the work
var result1 = task1.Result;
var result2 = task2.Result;
var result3 = task3.Result;
例如,要进行的特殊分组需要进行如下转换:

Task<Record[][]> GroupByKeyExcludingSomeGroups(IObservable<Record> source)
{
    return source
        .GroupBy(record => record.Key)
        .Select(grouped => grouped.ToArray())
        .Merge()
        .Where(array => array.All(r => !r.CompletedA && !r.CompletedB))
        .ToArray()
        .ToTask();
}
TaskGroupByKeyExcludingsomeGroups(IObservable源)
{
返回源
.GroupBy(record=>record.Key)
.Select(grouped=>grouped.ToArray())
.Merge()
.Where(array=>array.All(r=>!r.CompletedA&&!r.CompletedB))
.ToArray()
.ToTask();
}
当您将其合并到模式中时,它将如下所示:

Key,CompletedA,CompletedB
1,true,NULL
2,true,NULL
3,false,NULL
1,NULL,true
2,NULL,true  
Task<TResult> SomeRxTransformation1(IObservable<Record> source);
Task<Record[][]> task1 = GroupByKeyExcludingSomeGroups(subject);
source.ToObservable().Subscribe(subject); // This line does all the work
Record[][] result1 = task1.Result;
Task task1=groupbykey排除某些组(主题);
source.ToObservable().Subscribe(subject);//这条线做所有的工作
记录[][]result1=task1.Result;

当然,您也可以使用像dataflow或RR反应式扩展这样的管道处理器,但是,这完全是多余的,您可以在foreach循环中高效地执行,并且您可以先尝试一下records.CountBy(z=>new{Key=z.Key,Value=z.CompletedA??z.CompletedB})。其中(z=>z.Value==1)。选择(z=>z.Key)
可能会让你开始。你需要这个。有多少不同的