我有一个id (int
)列表,如下所示
[1,2,3,5,7,8,11,13,14..]
有没有快速/智能的方法(linq?)来获取所有的id,或者如果可能,获取范围?结果应该类似于[1-3, 5, 7-8, 11, 13-14]
。
当然,循环和计算int值以获得结果是很容易的,但我相信肯定有更简单的方法来做到这一点。
发布于 2017-10-15 12:15:49
免责声明这在大列表上是非常慢的,你也应该做一个不同的
这应该能起到作用
static void Main(string[] args)
{
//a list with a possible of duplicate
var theList = (new int[] { 1, 2, 3, 5, 7, 8, 11, 13, 14, 13 }).OrderBy(x => x).ToList();
var step1 = theList.Select((a, b) => theList.Skip(b).TakeWhile((x, y) => a == x || theList[b + y] - 1 == theList[b + y - 1]));
var step2 = step1.GroupBy(x => x.Last())
.Select(x => x.SelectMany(y => y).Distinct())
.Select(x => x.Count() > 1 ? string.Format("{0}-{1}", x.First(), x.Last()) : x.First().ToString());
var result = string.Format("[{0}]", string.Join(", ", step2));
}
发布于 2017-10-15 11:01:12
使用单一for循环的老式方法。
try
{
List<int> i = new List<int>() { 1, 2, 3, 5, 7, 8, 11, 13, 14 };
int istart = i[0];
bool flag = false;
// Use StringBuilder
for(int index = 0;index<i.Count-1;index++)
{
if ((i[index] + 1) == i[index + 1])
{
flag = true;
continue;
}
else
{
if (!flag)
Console.Write(istart);
else
Console.Write(istart + "-" + i[index]);
Console.Write(",");
flag = false;
istart = i[index + 1];
}
}
if (istart + 1 == i[i.Count - 1])
Console.Write(istart + "-" + i[i.Count - 1]);
else
Console.WriteLine(istart);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine();
Console.WriteLine("Done");
Console.Read();
输入List<int> i = new List<int>() { 1, 2, 3, 5, 7, 8, 11, 13, 14 };
输入List<int> i = new List<int>() { 1, 2, 3, 4, 5, 7, 8, 11, 13, 13 };
输入List<int> i = new List<int>() { 1, 4, 5, 7, 8, 9, 2, 13, 15, 17 };
发布于 2017-10-15 11:23:03
有一种linq方法可以做到这一点(如果数字不重复),但我不确定是否更容易:
int last = -1;
int rank = 0;
IEnumerable<string> grouped = arr
.GroupBy(i =>
{
rank += i - last - 1;
last = i;
return rank;
})
.Select(g => g.Count() == 1 ? g.First().ToString()
: g.First().ToString() + "-" + g.Last().ToString());
对我来说,这似乎相当复杂,比必要的资源密集型更多,也不灵活。Linq在许多情况下都很好用,但有时它就是不太适合。简单的循环有时是你能得到的最好的:
IEnumerable<string> Group(IEnumerable<int> sortedArr)
{
using (var en = sortedArr.GetEnumerator())
{
if (!en.MoveNext())
{
yield break;
}
int first = en.Current;
int last = first;
int count = 1;
while (true)
{
bool end;
if ((end = !en.MoveNext()) || en.Current - last > 1)
{
if (count == 1)
{
yield return first.ToString();
}
//else if (count == 2)
//{
// yield return first.ToString();
// yield return last.ToString();
//}
else
{
yield return first.ToString() + "-" + last.ToString();
}
if (end) { yield break; }
first = en.Current;
count = 1;
}
else
{
++count;
}
last = en.Current;
}
}
}
基准测试
让我们测量一下linq在10M阵列上的实际效率:
simple loop: 328MB, 1.2sec
my linq: 790MB, 2.7sec
Fredous linq: 1100MB, 7days (estimated)
代码:
int size = 10000000;
int[] arr = new int[size];
Random rnd = new Random(1);
arr[0] = 0;
for(int i = 1; i < size; ++i)
{
if (rnd.Next(100) < 25)
{
arr[i] = arr[i - 1] + 2;
}
else
{
arr[i] = arr[i - 1] + 1;
}
}
System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();
st.Start();
var res = Group(arr).ToList();
st.Stop();
MessageBox.Show(st.ElapsedMilliseconds.ToString());
MessageBox.Show(res.Sum(s => s.Length).ToString());// to be sure the work is done
https://stackoverflow.com/questions/46751027
复制相似问题