我需要过滤以下列表以返回以“Group”开头的第一个项目开头的所有项目,直至,但不包括以“Group”开头的下一个项目(或直至最后一个项目)
List<string> text = new List<string>();
text.Add("Group Hear It:");
text.Add(" item: The Smiths");
text.Add(" item: Fernando Sor");
text.Add("Group See It:");
text.Add(" item: Longmire");
text.Add(" item: Ricky Gervais Show");
text.Add(" item: In Bruges");
过滤后,我希望在第一个分组中包含以下项目:
"Group Hear It:" " item: The Smiths" " item: Fernando Sor"
第二组中的以下项目:
"Group See It:" " item: Longmire" " item: Ricky Gervais Show" " item: In Bruges"
这不起作用,因为我在第一个过滤列表的地方过滤了“item:”项目...我是否接近 TakeWhile ,或者离开?
var group = text.Where(t => t.StartsWith("Group ")))
.TakeWhile(t => t.ToString().Trim().StartsWith("item"));
答案 0 :(得分:11)
与杰夫·梅尔卡多一样,但没有预先处理整个可枚举:
public static class Extensions
{
public static IEnumerable<IList<T>> ChunkOn<T>(this IEnumerable<T> source, Func<T, bool> startChunk)
{
List<T> list = new List<T>();
foreach (var item in source)
{
if(startChunk(item) && list.Count > 0)
{
yield return list;
list = new List<T>();
}
list.Add(item);
}
if(list.Count > 0)
{
yield return list;
}
}
}
使用类似:
List<string> text = new List<string>();
text.Add("Group Hear It:");
text.Add(" item: The Smiths");
text.Add(" item: Fernando Sor");
text.Add("Group See It:");
text.Add(" item: Longmire");
text.Add(" item: Ricky Gervais Show");
text.Add(" item: In Bruges");
var chunks = text.ChunkOn(t => t.StartsWith("Group"));
答案 1 :(得分:2)
你可以在发电机的帮助下相当干净地做到这一点。生成器将跟踪当前正在使用的密钥,这是传统LINQ查询无法在不引入外部变量的情况下执行的操作。您只需要确定在您浏览集合时密钥何时应该更改。获得每个项目使用的密钥后,只需按该密钥对其进行分组即可。
public static class Extensions
{
public static IEnumerable<IGrouping<TKey, TResult>> ConsecutiveGroupBy<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, bool> takeNextKey,
Func<TSource, TKey> keySelector,
Func<TSource, TResult> resultSelector)
{
return
from kvp in AssignKeys(source, takeNextKey, keySelector)
group resultSelector(kvp.Value) by kvp.Key;
}
private static IEnumerable<KeyValuePair<TKey, TSource>> AssignKeys<TSource, TKey>(
IEnumerable<TSource> source,
Func<TSource, bool> takeNextKey,
Func<TSource, TKey> keySelector)
{
var key = default(TKey);
foreach (var item in source)
{
if (takeNextKey(item))
key = keySelector(item);
yield return new KeyValuePair<TKey, TSource>(key, item);
}
}
}
然后使用它:
var lines = new List<string>
{
"Group Hear It:",
" item: The Smiths",
" item: Fernando Sor",
"Group See It:",
" item: Longmire",
" item: Ricky Gervais Show",
" item: In Bruges",
};
var query = lines.ConsecutiveGroupBy(
line => line.StartsWith("Group"),
line => line,
line => line);
答案 2 :(得分:1)
一种方法是使用类并使用LINQ从类中获取结果:
public class MediaItem {
public MediaItem(string action, string name) {
this.Action = action;
this.Name = name;
}
public string Action = string.Empty;
public string Name = string.Empty;
}
List<MediaItem> mediaItemList = new List<MediaItem>();
mediaItemList.Add(new MediaItem("Group: Hear It", "item: The Smiths"));
mediaItemList.Add(new MediaItem("Group: Hear It", "item: Fernando Sor"));
mediaItemList.Add(new MediaItem("Group: See It", "item: Longmire"));
mediaItemList.Add(new MediaItem("Group: See It", "item: Ricky Gervais Show"));
mediaItemList.Add(new MediaItem("Group: See It", "item: In Bruges"));
var results = from item in mediaItemList.AsEnumerable()
where item.Action == "Group: Hear It"
select item.Name;
foreach (string name in results) {
MessageBox.Show(name);
}
另一种方法是单独使用LINQ:
// Build the list
List<string> text = new List<string>();
text.Add("Group Hear It:");
text.Add(" item: The Smiths");
text.Add(" item: Fernando Sor");
text.Add("Group See It:");
text.Add(" item: Longmire");
text.Add(" item: Ricky Gervais Show");
text.Add(" item: In Bruges");
text.Add("Group Buy It:");
text.Add(" item: Apples");
text.Add(" item: Bananas");
text.Add(" item: Pears");
// Query the list and create a "table" to work with
var table = from t in text
select new {
Index = text.IndexOf(t),
Item = t,
Type = t.Contains("Group") ? "Group" : "Item",
GroupIndex = t.Contains("Group") ? text.IndexOf(t) : -1
};
// Get the table in reverse order to assign the correct group index to each item
var orderedTable = table.OrderBy(i => i.Index).Reverse();
// Update the table to give each item the correct group index
table = from t in table
select new {
Index = t.Index,
Item = t.Item,
Type = t.Type,
GroupIndex = t.GroupIndex < 0 ?
orderedTable.Where(
i => i.Type == "Group" &&
i.Index < t.Index
).First().Index :
t.GroupIndex
};
// Get the "Hear It" items from the list
var hearItItems = from g in table
from i in table
where i.GroupIndex == g.Index &&
g.Item == "Group Hear It:"
select i.Item;
// Get the "See It" items from the list
var seeItItems = from g in table
from i in table
where i.GroupIndex == g.Index &&
g.Item == "Group See It:"
select i.Item;
// Get the "Buy It" items I added to the list
var buyItItems = from g in table
from i in table
where i.GroupIndex == g.Index &&
g.Item == "Group Buy It:"
select i.Item;
答案 3 :(得分:1)
试试这个:
var i = 0;
var groups = text.GroupBy(t => t.StartsWith("Group") ? ++i : i);
我持有我们看过小组状况的次数。使用i ++代替++我会让条件完成一个组而不是启动它。
答案 4 :(得分:0)
您可能需要命令式代码来执行此操作,我无法想到LINQ解决方案。
List<List<string>> results = new List<List<string>>();
List<string> currentGroup = null;
foreach (var item in text)
{
if (item.StartsWith("Group"))
{
if (currentGroup != null) results.Add(currentGroup);
currentGroup = new List<string>();
}
currentGroup.Add(item);
}
results.Add(currentGroup);
答案 5 :(得分:0)
您无法使用干净的LINQ和lambda表达式执行此操作。你可以定义一个委托方法来访问一个位于外部作用域的布尔标志(比如在执行它的实例上),然后将它传递给select语句,但我认为正常的for循环会更好。如果你这样做,你可以只注意起始和结束索引然后提取范围,它可能是最干净的解决方案。