我有一份清单。我想从每次类似元素的运行中获取最后一个值。
我的意思是什么?让我举一个简单的例子。给出单词列表
['golf','hip','hop','hotel','grass','world','wee']
相似度函数'以相同的字母开头',该函数将返回较短的列表
['golf','hotel','grass','wee']
为什么呢?原始列表包含1个G字,3个H字,1个G字和2个W字。该函数返回每次运行的最后一个单词。
我该怎么做?
假设的C#语法(实际上我正在使用客户对象,但我想分享一些你可以运行并自己测试的东西)
> var words = new List<string>{"golf", "hip", "hop", "hotel", "grass", "world", "wee"};
> words.LastDistinct(x => x[0])
["golf", "hotel", "grass", "wee"]
编辑:我试过了.GroupBy(x => x[0]).Select(g => g.Last())
,但这给了''草',
'hotel','wee'] 不我想要的东西。仔细阅读例子。
编辑。另一个例子。
['apples','armies','black','beer','bastion','cat','cart','able','art','bark']
这里有5次运行(A的运行,B的运行,C的运行,A的新的运行,B的新运行) 。每次运行的最后一个词是:
['armies','bastion','cart','art','bark']
要理解的重要一点是每次运行都是独立的。不要在开始时将A的运行与A的运行混合在一起。
答案 0 :(得分:1)
您可以使用此扩展程序,该扩展程序可以按相邻/连续元素进行分组:
public static IEnumerable<IGrouping<TKey, TSource>> GroupAdjacent<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector)
{
TKey last = default(TKey);
bool haveLast = false;
List<TSource> list = new List<TSource>();
foreach (TSource s in source)
{
TKey k = keySelector(s);
if (haveLast)
{
if (!k.Equals(last))
{
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
list = new List<TSource>();
list.Add(s);
last = k;
}
else
{
list.Add(s);
last = k;
}
}
else
{
list.Add(s);
last = k;
haveLast = true;
}
}
if (haveLast)
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
}
public class GroupOfAdjacent<TSource, TKey> : IEnumerable<TSource>, IGrouping<TKey, TSource>
{
public TKey Key { get; set; }
private List<TSource> GroupList { get; set; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.Generic.IEnumerable<TSource>)this).GetEnumerator();
}
System.Collections.Generic.IEnumerator<TSource> System.Collections.Generic.IEnumerable<TSource>.GetEnumerator()
{
foreach (var s in GroupList)
yield return s;
}
public GroupOfAdjacent(List<TSource> source, TKey key)
{
GroupList = source;
Key = key;
}
}
然后很容易:
var words = new List<string>{"golf", "hip", "hop", "hotel", "grass", "world", "wee"};
IEnumerable<string> lastWordOfConsecutiveFirstCharGroups = words
.GroupAdjacent(str => str[0])
.Select(g => g.Last());
输出:
string.Join(",", lastWordOfConsecutiveFirstCharGroups); // golf,hotel,grass,wee
您的其他样本:
words=new List<string>{"apples", "armies", "black", "beer", "bastion", "cat", "cart", "able", "art", "bark"};
lastWordOfConsecutiveFirstCharGroups = words
.GroupAdjacent(str => str[0])
.Select(g => g.Last());
输出:
string.Join(",", lastWordOfConsecutiveFirstCharGroups); // armies,bastion,cart,art,bark
答案 1 :(得分:1)
以老式的方式做这件事并不复杂:
Func<string, object> groupingFunction = s => s.Substring(0, 1);
IEnumerable<string> input = new List<string>() {"golf", "hip", "..." };
var output = new List<string>();
if (!input.Any())
{
return output;
}
var lastItem = input.First();
var lastKey = groupingFunction(lastItem);
foreach (var currentItem in input.Skip(1))
{
var currentKey = groupingFunction(str);
if (!currentKey.Equals(lastKey))
{
output.Add(lastItem);
}
lastKey = currentKey;
lastItem = currentItem;
}
output.Add(lastItem);
您还可以将其转换为Tim Schmelter has done的通用扩展方法;我已经采取了一些措施来有目的地概括代码(使用object
作为密钥类型,IEnumerable<T>
作为输入类型。
答案 2 :(得分:0)
您可以使用以下扩展方法在某些条件下将序列拆分为组(即子序列):
public static IEnumerable<IEnumerable<T>> Split<T, TKey>(
this IEnumerable<T> source, Func<T, TKey> keySelector)
{
var group = new List<T>();
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
yield break;
else
{
TKey currentKey = keySelector(iterator.Current);
var keyComparer = Comparer<TKey>.Default;
group.Add(iterator.Current);
while (iterator.MoveNext())
{
var key = keySelector(iterator.Current);
if (keyComparer.Compare(currentKey, key) != 0)
{
yield return group;
currentKey = key;
group = new List<T>();
}
group.Add(iterator.Current);
}
}
}
if (group.Any())
yield return group;
}
获得预期结果如下:
string[] words = { "golf", "hip", "hop", "hotel", "grass", "world", "wee" };
var result = words.Split(w => w[0])
.Select(g => g.Last());
结果:
golf
hotel
grass
wee
答案 3 :(得分:0)
试试这个算法
var words = new List<string> { "golf", "hip", "hop", "hotel", "grass", "world", "wee" };
var newList = new List<string>();
int i = 0;
while (i < words.Count - 1 && i <= words.Count)
{
if (words[i][0] != words[i+1][0])
{
newList.Add(words[i]);
i++;
}
else
{
var j = i;
while ( j < words.Count - 1 && words[j][0] == words[j + 1][0])
{
j++;
}
newList.Add(words[j]);
i = j+1;
}
}
答案 4 :(得分:0)
因为您的输入是List&lt;&gt;,所以我认为这应该对您有效,并且具有可接受的性能,特别是它非常简洁:
var result = words.Where((x, i) => i == words.Count - 1 ||
words[i][0] != words[i + 1][0]);
如果您愿意,可以在结果上附加ToList()
以获得List<string>
。
答案 5 :(得分:0)
我去了
/// <summary>
/// Given a list, return the last value from each run of similar items.
/// </summary>
public static IEnumerable<T> WithoutDuplicates<T>(this IEnumerable<T> source, Func<T, T, bool> similar)
{
Contract.Requires(source != null);
Contract.Requires(similar != null);
Contract.Ensures(Contract.Result<IEnumerable<T>>().Count() <= source.Count(), "Result should be at most as long as original list");
T last = default(T);
bool first = true;
foreach (var item in source)
{
if (!first && !similar(item, last))
yield return last;
last = item;
first = false;
}
if (!first)
yield return last;
}