查找序列中的所有非最后序列

时间:2009-05-12 08:09:07

标签: c# list

我似乎无法绕过这个......我的序列看起来有点像这样:

A  1  1  50
A  1  2  50
A  2  1  20
A  2  2  60
B  1  1  35
B  1  2  35
C  1  1  80
D  1  1  12
D  1  2  12
D  1  3  15
D  2  1  12

我需要做的是将最后一列的值设置为0,它们不是最后一个值。所以例如我需要将A11设置为0,因为有A12。我需要将A21设置为0,因为有A22。 B11必须为0,因为有B12。留下C11,因为没有C12。由于D13,D11和D12变为0,并且D21保持不变。

在实际数据集中,第1列是用户名,第2列是日期,第3列是登录时间,最后一列是金额。我需要将特定用户在特定日期的上次登录时间与之无关的所有金额设置为0。

任何能够轻松完成此任务的人?我正在考虑多个嵌套的foreach语句或加入和分组等等,但我无法决定如何做到最好......或者如何真正做到这一点......

(如果你想出一个更好的标题,请编辑我的标题!)


更多信息

对不起,我当然应该告诉你更多关于这种语言的内容。我正在使用C#和.NET 3.5。我正在使用Linq to SQL从数据库中获取数据,并且我将所有数据作为对象(简单类型的容器类型类)在通用List中。我希望我可以将查询调整为仅获取最后一行,但问题是此数据将进入报告,并且必须显示所有行。我希望我能在报告中做到这一点,但这似乎是not so easy。或者至少我不知道如何做到这一点,也没有得到任何有效的答案。所以这就是我想我需要复制该列并清除那些重复值的原因。这样我就可以显示包含所有值的列,然后对只有最后一个值的列进行求和。希望有道理:P

哦,并且还要在单独的列中清除日期和登录时间问题:事情是它们都是DateTimes,并且日期不需要与登录日期时间具有相同的日期。这是一个奇怪的问题,因为我每天工作的时间可能不需要是00:00到23:59。例如,一天可能在凌晨3点开始。


更新

刚才发现整个问题可能会以不同的方式解决......(甚至应该是这样)通过创建一个新列(在我的情况下是一个新属性),然后只将那些最后的值复制到其中新财产。但是,我必须再次找到所有这些价值......那就像我已经拥有的那个问题一样,但有点倒退或者称之为什么。

6 个答案:

答案 0 :(得分:1)

如果数据按升序用户/日期/时间顺序排列,那么简单地说:

    Foo lastRow = null;
    foreach (Foo row in list)
    {
        if (lastRow != null && row.User == lastRow.User
            && row.Date == lastRow.Date)
        {
            lastRow.Value = 0;
        }
        lastRow = row;
    }

答案 1 :(得分:1)

假设DataTable未排序,您可以将行复制到数组中,然后使用Array.Sort函数对其进行排序,以便按正确的顺序将所有用户登录组合在一起。然后只需传递行并将值设置为最后一个实例的0 excpet。

例如:

    private void Filter(DataTable tbl)
    {
        DataRow[] rows = new DataRow[tbl.Rows.Count];
        rows.CopyTo(rows, 0);
        Array.Sort<DataRow>(rows, FilterOrder);

        for (int i = 0; i < rows.Length - 1; i++)
        {
            if ((string)rows[i][0] != (string)rows[i + 1][0])
                continue;
            if ((DateTime)rows[i][1] != (DateTime)rows[i + 1][1])
                continue;
            rows[i][3] = 0;
        }
    }

    private int FilterOrder(DataRow row1, DataRow row2)
    {
        string r1c1 = (string)row1[0];
        string r2c1 = (string)row2[0];
        if (r1c1 != r2c1) return r1c1.CompareTo(r2c1);

        DateTime r1c2 = (DateTime)row1[1];
        DateTime r2c2 = (DateTime)row2[1];
        if (r1c2 != r2c2) return r1c2.CompareTo(r2c2);

        DateTime r1c3 = (DateTime)row1[2];
        DateTime r2c3 = (DateTime)row2[2];
        return r1c3.CompareTo(r2c3);
    }

答案 2 :(得分:0)

如果您使用的是.NET 3.5,那么LINQ可能会为您提供构建合理查询的工具,这些查询比一堆嵌套的foreach循环更难以理解。

假设您的数据在数据库中,您可以使用LINQ to SQL或LINQ to Entities构建强类型对象来表示数据库表中的记录。

此课程是否准确代表您正在使用的数据?

class User
{
    public string Username { get; set; }
    public DateTime LastLoginDateTime { get; set; }
    public int LoginCount { get; set; }
}

答案 3 :(得分:0)

这不是世界上最干净的代码,但这将完成它。你应该考虑一两种方法,我把它留给你。

    static void ClearRepeatValues()
    {
        var arr = new[] {
        new [] {"A","1","1","50",},
        new [] {"A","1","2","50",},
        new [] {"A","2","1","20",},
        new [] {"A","2","2","60",},
        new [] {"B","1","1","35",},
        new [] {"B","1","2","35",},
        new [] {"C","1","1","80",},
        new [] {"D","1","1","12",},
        new [] {"D","1","2","12",},
        new [] {"D","1","3","15",},
        new [] {"D","2","1","12"}
        };

        if (arr == null || arr.Length == 0)
        {
            return;
        }
        var lastRow = arr[0];
        for (int i = 1; i < arr.Length; i++)
        {
            var currentRow = arr[i];
            if (lastRow[0] == currentRow[0] && lastRow[1] == currentRow[1])
            {
                lastRow[3] = "0";
            }
            lastRow = currentRow;
        }
    }

但是,简单的SQL查询可能是获取您关注的值的更好方法,EG:

select * from Session s1 where s1.Id in 
(select top 1 s2.Id from Session s2 where s2.User = s1.User order by s2.Date)   

答案 4 :(得分:0)

这是我的SQL Server兼容解决方案。

您可以在下面找到表格布局。

解决方案

我的想法是找出您感兴趣的colums,将该结果集与表一起加入,并修改表中所有其他行:

with SequencesToKeep(col1, col2, col3Max) as
(
    select 
        col1, 
        col2, 
        max(col3) col3Max 
    from 
        sequences
    group by 
        col1, 
        col2
)
update
    sequences
set
    col4 = 0
from
    sequences s
    left join SequencesToKeep sk 
        on  s.col1 = sk.col1 
        and s.col2 = sk.col2
        and s.col3 = sk.col3Max
where
    sk.col1 is null

结果

select * from sequences

col1 col2        col3        col4
---- ----------- ----------- -----------
A    1           1           0
A    1           2           50
A    2           1           0
A    2           2           60
B    1           1           0
B    1           2           35
C    1           1           80
D    1           1           0
D    1           2           0
D    1           3           15
D    2           1           12

表格布局和演示数据

create table sequences (col1 varchar(1), col2 int, col3 int, col4 int)
go
insert into sequences values ('A', 1, 1, 50)
insert into sequences values ('A', 1, 2, 50)
insert into sequences values ('A', 2, 1, 20)
insert into sequences values ('A', 2, 2, 60)
insert into sequences values ('B', 1, 1, 35)
insert into sequences values ('B', 1, 2, 35)
insert into sequences values ('C', 1, 1, 80)
insert into sequences values ('D', 1, 1, 12)
insert into sequences values ('D', 1, 2, 12)
insert into sequences values ('D', 1, 3, 15)
insert into sequences values ('D', 2, 1, 12)
go

答案 5 :(得分:0)

好的,我决定反过来做。我添加了另一列,然后运行此代码:

data
    .GroupBy(x => new
        {
            x.Col1,
            x.Col2,
        })
    .Select(x => x.MaxBy(y => y.Col3)
    .ForEach(x =>
        {
            x.Col5 = x.Col4,
        });

这个解决方案并没有真正回答我原来的问题。但是,基于我的方式,给出一个关于如何做到这一点的建议:

  • 像其他例子一样分组
  • 对于基于Col3按降序排序的每个组序列
    • 将除第一项之外的所有项目设置为0(可能只是使用bool或其他内容)

未经测试,但在我看来,这至少应该起作用:p

注意: MaxByForEach方法来自MoreLinq

相关问题