首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何根据属性和其他属性值的顺序值在C# linq中进行分组?

如何根据属性和其他属性值的顺序值在C# linq中进行分组?
EN

Stack Overflow用户
提问于 2017-01-16 19:06:29
回答 3查看 635关注 0票数 1

这是一个类的IEnumerable。

代码语言:javascript
运行
复制
IEnumerable<Card> cardList = new List<Card> {
            new Card { CardNumber = 1234, Amount = 10m, DisplayText = "" },
            new Card { CardNumber = 1235, Amount = 10m, DisplayText = "" },
            new Card { CardNumber = 1236, Amount = 10m, DisplayText = "" },
            new Card { CardNumber = 1237, Amount = 10m, DisplayText = "" },
            new Card { CardNumber = 1238, Amount = 10m, DisplayText = "" },
            new Card { CardNumber = 1239, Amount = 15m, DisplayText = "" },
            new Card { CardNumber = 1240, Amount = 10m, DisplayText = "" },
            new Card { CardNumber = 1241, Amount = 10m, DisplayText = "" },
            new Card { CardNumber = 1242, Amount = 25m, DisplayText = "" },
            new Card { CardNumber = 1243, Amount = 25m, DisplayText = "" },
            new Card { CardNumber = 1244, Amount = 25m, DisplayText = "" },
            new Card { CardNumber = 1245, Amount = 25m, DisplayText = "" }
        };

我想要完成的是按数量和顺序卡片号对列表进行分组,而组中至少有4张卡,否则就不会分组。这是我想要达到的目标的一个例子。结果将是另一个IEnumerable,并包含以下内容

代码语言:javascript
运行
复制
    Card { CardNumber = null, Amount = 10m, DisplayText = "1234 - 1238" },
    Card { CardNumber = null, Amount = 15m, DisplayText = "1239" },
    Card { CardNumber = null, Amount = 10m, DisplayText = "1240" },
    Card { CardNumber = null, Amount = 10m, DisplayText = "1241" },
    Card { CardNumber = null, Amount = 25m, DisplayText = "1242 - 1245" }

希望我能做到这一点。任何帮助都将不胜感激。

谢谢,

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-01-16 20:10:04

纯LINQ运算符是不可能的。但是,可以使用混合标准和自定义LINQ类扩展方法。

让我们创建一个自定义方法,该方法允许我们根据接收、先前当前元素的谓词拆分序列:

代码语言:javascript
运行
复制
public static class Extensions
{
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, Func<T, T, bool> splitOn)
    {
        using (var e = source.GetEnumerator())
        {
            for (bool more = e.MoveNext(); more; )
            {
                var last = e.Current;
                var group = new List<T> { last };
                while ((more = e.MoveNext()) && !splitOn(last, e.Current))
                    group.Add(last = e.Current);
                yield return group;
            }
        }
    }
}

现在您可以使用以下查询来完成您的目标:

代码语言:javascript
运行
复制
var result = cardList.OrderBy(c => c.CardNumber)
    .Split((prev, next) => prev.Amount != next.Amount || prev.CardNumber + 1 != next.CardNumber)
    .SelectMany(g => g.Count() >= 4 ?
        new [] { new Card { Amount = g.First().Amount, DisplayText = g.First().CardNumber + " - " + g.Last().CardNumber } } :
        g.Select(c => new Card { Amount = c.Amount, DisplayText = c.CardNumber.ToString() }));

OrderBy后面跟着自定义Split进行初始分组。剩下的棘手部分是如何基于Count标准对组/取消组元素进行分组,这是通过在一种情况下生成单个项(通过选择单个项数组)的条件SelectMany方法实现的,或者是通过使用内部Select在另一种情况下扁平组来实现的。

票数 2
EN

Stack Overflow用户

发布于 2017-01-16 19:37:41

我不认为这样的事情可以用LINQ来完成。

但是如果没有LINQ,这是很容易实现的,比如:(我假设cardList是由CardNumber命令的)

代码语言:javascript
运行
复制
List<Card> result = new List<Card>();

Func<List<Card>,List<Card>> bufferToResult = (buf) =>
{
    List<Card> res = new List<Card>();
    if(buf.Count >= 4) {
        string text = buf[0].CardNumber + " - " + buf[buf.Count-1].CardNumber;
        Card newCard = new Card { Amount = buf[0].Amount, DisplayText = text };
        res.Add(newCard);
    } else {
        foreach(Card c in buf) {
            Card newCard = new Card { Amount = c.Amount, DisplayText = c.CardNumber.ToString() };
            res.Add(newCard);
        }
    }
    return res;
};

List<Card> buffer = new List<Card>();
for(int i=0; i<cardList.Count(); ) {
    Card card = cardList.ElementAt(i);
    if(buffer.Count == 0) {
        buffer.Add(card);
        i++;
    } else if(card.Amount == buffer[0].Amount) {
        buffer.Add(card);
        i++;
    } else {
        result.AddRange(bufferToResult(buffer));
        buffer.Clear();
    }
}
if(buffer.Count > 0)
    result.AddRange(bufferToResult(buffer));
票数 0
EN

Stack Overflow用户

发布于 2017-01-16 20:57:58

您可以通过将通过约束进行的项分组到一个新列表中,然后根据组大小填充第二个列表来实现这一点:

代码语言:javascript
运行
复制
IList<Card> group = new List<Card>();
IList<Card> result = new List<Card>();
for (int i = 0, j = 0; i < cardList.Count - 1; i++)
{
    group.Clear();
    group.Add(cardList[i]);
    for (j = i + 1; j < cardList.Count; j++, i++)
    {
        if (cardList[j].Amount == cardList[i].Amount && cardList[j].CardNumber - cardList[i].CardNumber == 1)
        {
            group.Add(cardList[j]);
        }
        else break;
    }

    if (4 > group.Count)
    {
        foreach (var item in group)
        {
            result.Add(new Card { Amount = item.Amount, DisplayText = item.CardNumber.ToString() });
        }
    }
    else
    {
        result.Add(new Card { Amount = group[0].Amount, DisplayText = string.Format("{0} - {1}", group[0].CardNumber, group.Last().CardNumber) });
    }
}

使用LINQPad的结果:

代码语言:javascript
运行
复制
CardNumber  Amount  DisplayText
null        10      1234 - 1238
null        15      1239
null        10      1240
null        10      1241
null        25      1242 - 1245

比起过于复杂的Linq代码,我更喜欢这个。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41683387

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档