如何在给定的整数列表中找到连续的段对(功能上没有迭代)?

时间:2016-08-18 13:36:24

标签: c# functional-programming

我有{1,2,3,5,8,9,10}之类的整数列表,我需要以下结果 [1,3],[5,5],[8,10]

我使用命令式样式编写了解决方案,但我想要一个与函数式编程一致的解决方案。

我的必要解决方案:

    public static List<ContinuousNotificationSegment> ConvertToNotificationSegment(this List<NotificationDTO> input)
    {
        var sortedNotificationList = input.Select(n => n.ID).ToList();
        sortedNotificationList = sortedNotificationList.OrderBy(n => n).ToList();
        List<ContinuousNotificationSegment> continuousSegments = new List<ContinuousNotificationSegment>();

        long continuousSegmentStart = 0, continuousSegmentEnd = 0;

        for (int i = 0; i < sortedNotificationList.Count; i++)
        {                
            if (IsContinuous(sortedNotificationList[i], ((i + 1) < sortedNotificationList.Count ? sortedNotificationList[i + 1] : -999)))
            {
                continuousSegmentStart = continuousSegmentStart == 0 ? sortedNotificationList[i] : continuousSegmentStart;
            }
            else
            {
                continuousSegmentEnd = sortedNotificationList[i];

                continuousSegments.Add(new ContinuousNotificationSegment
                {
                    MinNotificationId = continuousSegmentStart == 0 ? continuousSegmentEnd : continuousSegmentStart,
                    MaxNotificationId = continuousSegmentEnd
                });

                continuousSegmentStart = 0;
            }
        }

        return continuousSegments;
    }

    private static bool IsContinuous(long prevValue, long nextValue)
    {
        return nextValue - prevValue == 1;
    }

4 个答案:

答案 0 :(得分:2)

DateTime

有效。我明白了:

result

答案 1 :(得分:1)

您可以通过首先排序数字列表,然后选择当前数字和前一个数字之间的差值大于1时递增的数字和分组ID来实现。然后只对该分组ID进行分组并采取最小和最大数字作为范围。

public static IEnumerable<Tuple<int, int>> Runs(this IEnumerable<int> nums)
{
    int? prev = null;
    int group = 0;
    return nums.OrderBy(n => n)
        .Select(
            n =>
            {
                if (prev.HasValue && n - prev.Value > 1) { group++; }
                prev = n;
                return new { group, num = n };
            })
        .GroupBy(x => x.group)
        .Select(g => new Tuple<int, int>(g.Min(x => x.num), g.Max(x => x.num)));
}

这段代码

var nums = new[] { 1, 2, 3, 5, 7, 8, 9 };
Console.WriteLine(string.Join(";", nums.Runs()));

输出

  

(1,3);(5,5);(7,9)

答案 2 :(得分:1)

因为我已经被教导在功能上编程它应该看起来像(警告,它是[伪代码]!和[尾递归]!)

GetRanges(input)
    if (input == null || input.Count == 0) 
        return {};

    var ranges = {};
    GetRangesInternal(ranges, {null, null}, input);
    return ranges;

GetRangesInternal(ranges, {initialElement, lastElement}, input) =>
    var head = input.Take(1);
    var tail = input.Skip(1);

    // no first element - prepare new range and move on
    if (initialElement == null)
        GetRangesInternal(ranges, {head, head}, tail);
        return;

    // range continued - update it and move on
    if (head == lastElement+1)
        GetRangesInternal(ranges, {initialElement, head}, tail)
        return;

    // range stopped - add it to result
    ranges.Add({initialElement, lastElement});
    GetRangesInternal(ranges, {head, head}, tail);

GetRanges是入口点

答案 3 :(得分:0)

我能想出的最紧凑,最清晰的LINQ是:

var intList = new List<int> { 1, 2, 3, 5, 8, 9, 10 };

var agg = intList.Aggregate(new List<List<int>> { new List<int>() }, (i, j) =>
    {
        if (!i.Last().Any() || i.Last().Last() + 1 == j)
            i.Last().Add(j);
        else
            i.Add(new List<int> { j });
        return i;
    }).Select(i => new List<int> { i.First(), i.Last() });

结果完全如问题所述。