我有一个列表,当多个项目具有相同名称时,我想在其中添加一个内部计数器。
var myList = new List<string>();
myList.Add("a");
myList.Add("b");
myList.Add("b");
myList.Add("c");
在一些花哨的 LINQ 东西之后,我希望结果是 a01 b01 b02 c01 。
有什么好主意吗?
如果您想保留订单,在 OOTB LINQ 中没有非常好的方法可以做到这一点,但是您可以敲出类似的东西
public static IEnumerable<TResult> SelectWithUniquifier<TSource, TResult>(
this IEnumerable<TSource> source, Func<TSource, int, TResult> uniquifier)
{
Dictionary<TSource, int> counter = new Dictionary<TSource, int>();
foreach(TSource s in source)
{
int thisIndex = counter.ContainsKey(s) ? counter[s] : 0;
counter[s] = thisIndex + 1;
yield return uniquifier(s, thisIndex);
}
}
只是有一个更好的名字。
对于您的示例,您将拥有
var result = myList.SelectWithUniquifier((s, i) => s + (i+1).ToString("00"));
因为你得到的索引是从零开始的。
并不是说这很好,但它(主要是)Linq 解决方案:
var indexed = from index in myList.Aggregate(
new
{
Counters = new Dictionary<string, int>(),
Items = new List<string>()
},
(acc, cur) =>
{
if (!acc.Counters.ContainsKey(cur))
acc.Counters.Add(cur, 0);
acc.Counters[cur] = acc.Counters[cur] + 1;
acc.Items.Add(cur + acc.Counters[cur]);
return acc;
}).Items
select index;
累加部分非常难看,但它完成了工作,并且全部在 Linq 计算中。
编辑
如果初始列表已经排序,则此表达式更清晰(但可能效率低下,您必须查看列表中有多少项目):
var indexed = from index in myList.Aggregate(
new
{
Counter = 0,
Key = (string)null,
Items = Enumerable.Empty<string>()
},
(acc, cur) =>
{
var counter = acc.Key != cur ? 1 : acc.Counter+1;
return new
{
Counter = counter,
Key = cur,
Items = acc.Items.Concat(
Enumerable.Repeat(cur + counter, 1))
};
}).Items
select index;
另一种更简单的方法:
var result = myList.GroupBy(x => x)
.SelectMany(g => g.Select((x, i) => x + (i + 1).ToString("00")));
请参阅其他答案以了解一些花哨(而且非常令人困惑)的 LINQ 解决方案。如果您不一定需要使用 LINQ:
var myList = new List<string> { "a", "b", "c", "b" };
var counter = new ConcurrentDictionary<string, int>();
for (int i = 0; i < myList.Count; i++)
{
var currVal = myList[i];
counter.AddOrUpdate(currVal, 1, (value, count) => count + 1);
myList[i] = currVal + counter[currVal].ToString("00");
}
ConcurrentDictionary
不需要,您可以手动执行“添加或更新”操作,具体取决于您对速度与代码清晰度的重视程度。无论哪种方式,在我看来,这是一种更具可读性和可维护性的方式来做你想做的事情。不要害怕循环。:)
当然,这可以作为扩展方法或某些实用程序类的静态方法等来完成。
var res =
myList
.GroupBy(item => item)
.Select(item => String.Format("{0}{1}", item.Key, item.Count()));