将范围序列拆分为多个字符串c#,linq

时间:2016-01-30 16:19:41

标签: c# linq

不确定为什么问题被标记为offtopic,问题帖子中包含所谓的期望行为!

我正在尝试编写这个带有两个输入的程序:
•一组包含间隔
•和一组排除间隔

可以按任何顺序给出间隔集,它们可以是空的或重叠的。程序应输出取得所有包含的结果并“删除”排除。输出应按排序顺序作为非重叠间隔给出。

间隔仅包含整数

示例:

包括:50-600,10-100
不包括:(空)
输出:10-600

包括:10-100,200-300,400-600
不包括:95-205,410-420
输出:10-94,206-300,400-409,421-600

我尝试从include和exclude中填充两个Enumerable Range(在拆分,解析之后),但之后没有找到任何有效的方法来实现它。

string[] _break = _string.Split(',');
string[] _breakB = _stringB.Split(',');
string[] res = new string[_break.Length + 1];
string[] _items, _itemsB;
List < int > _back = new List < int > ();
int count = 0;

foreach(var _item in _break) {
    _items = _item.Split('-');
    var a = Enumerable.Range(int.Parse(_items[0]), (int.Parse(_items[1]) - int.Parse(_items[0]) + 1)).ToList();

    foreach(var _itemB in _breakB) {

        _itemsB = _itemB.Split('-');

        var b = Enumerable.Range(int.Parse((_itemsB[0])), (int.Parse(_itemsB[1]) - int.Parse((_itemsB[0])) + 1)).ToList();

        var c = a.Except < int > (b).ToList();

        /// different things tried here, but they are not good

        res[count] = c.Min().ToString() + "-" + c.Max().ToString();
        count++;

    }
}
return res;

任何输入都会有很大的帮助

4 个答案:

答案 0 :(得分:2)

您可以使用内置Json : { "shipmentsAllInfo": { "shipmentSingle": [ { "delivered": "5", "exception": "4", "intransit": "2", "manifest": "1", "outForDelivery": "3", "shipmentDetails": [ { "referenceNumbers": "2", "trackingNumber": "1" }, { "referenceNumbers": "4", "trackingNumber": "3" } ], "total": "7", "upsAccount": "Andrew", "voids": "6" }, { "delivered": "5", "exception": "4", "intransit": "2", "manifest": "1", "outForDelivery": "3", "shipmentDetails": [ { "referenceNumbers": "2", "trackingNumber": "1" }, { "referenceNumbers": "4", "trackingNumber": "3" } ], "total": "7", "upsAccount": "Andrew", "voids": "6" } ] } } 集合为您完成大部分工作:

SortedSet<T>集合实现了有用的SortedSet<T>UnionWith方法,至少使代码非常容易理解:

ExceptWith

Application output

答案 1 :(得分:1)

这应该比SortedSet技巧更快,至少对于大间隔。想法就像:

enter image description here

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Test
{
    using Pair = Tuple<int, int>;   //for brevity

    struct Point    //point of an interval
    {
        public enum Border { Left, Right };
        public enum Interval { Including, Excluding };
        public int Val;
        public int Brdr;
        public int Intr;
        public Point(int value, Border border, Interval interval)
        {
            Val = value;
            Brdr = (border == Border.Left) ? 1 : -1;
            Intr = (int)interval;
        }
        public override string ToString() =>
            (Brdr == 1 ? "L" : "R") + (Intr == 0 ? "+ " : "- ") + Val;
    }

    class Program
    {
        static IEnumerable<Pair> GetInterval(string strIn, string strEx)
        {
            //a func to get interval border points from string:
            Func<string, Point.Interval, IEnumerable<Point>> parse = (str, intr) =>
               Regex.Matches(str, "[0-9]+").Cast<Match>().Select((s, idx) =>
               new Point(int.Parse(s.Value), (Point.Border)(idx % 2), intr));

            var INs = parse(strIn, Point.Interval.Including);
            var EXs = parse(strEx, Point.Interval.Excluding);

            var intrs = new int[2];  //current interval border control IN[0], EX[1]
            int start = 0;       //left border of a new resulting interval
            //put all points in a line and loop:
            foreach (var p in INs.Union(EXs).OrderBy(x => x.Val))
            {
                //check for start (close) of a new (cur) interval:
                var change = (intrs[p.Intr] == 0) ^ (intrs[p.Intr] + p.Brdr == 0);
                intrs[p.Intr] += p.Brdr;
                if (!change) continue;

                var In = p.Intr == 0 && intrs[1] == 0;  //w no Ex
                var Ex = p.Intr == 1 && intrs[0] > 0;   //breaks In
                var Open = intrs[p.Intr] > 0;
                var Close = !Open;

                if (In && Open || Ex && Close)
                {
                    start = p.Val + p.Intr; //exclude point if Ex
                }
                else if (In && Close || Ex && Open)
                {
                    yield return new Pair(start, p.Val - p.Intr);
                }
            }
        }

        static void Main(string[] args)
        {
            var strIN = "10-100, 200-300, 400-500, 420-480";
            var strEX = "95-205, 410-420";

            foreach (var i in GetInterval(strIN, strEX))
                Console.WriteLine(i.Item1 + "-" + i.Item2);

            Console.ReadLine();
        }
    }
}

答案 2 :(得分:1)

因此,您可以将任务分离到子任务列表:

  1. 将间隔的源行解析为对象列表
  2. 如果他们越过
  3. ,则将其间隔
  4. 从'include'
  5. 中排除区间'排除'

    我在这里发布了我的结果代码:http://rextester.com/OBXQ56769

    代码也可以进行优化,但我希望它非常简单。希望它会对你有所帮助。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text.RegularExpressions;
    
    namespace ConsoleApplication
    {
        public class Program
        {
            private const string Includes = "10-100, 200-300, 400-500 ";
            private const string Excludes = "95-205, 410-420";
            private const string Pattern = @"(\d*)-(\d*)";
    
            public static void Main(string[] args)
            {
                var includes = ParseIntevals(Includes);
                var excludes = ParseIntevals(Excludes);
    
                includes = ConcatinateIntervals(includes);
                excludes = ConcatinateIntervals(excludes);
    
                // The Result
                var result = ExcludeFromInclude(includes, excludes);
    
                foreach (var interval in result)
                {
                    Console.WriteLine(interval.Min + "-" + interval.Max);
                }            
            }
    
    
            /// <summary>
            /// Excludes intervals 'excludes' from 'includes'
            /// </summary>
            public static List<Interval> ExcludeFromInclude(List<Interval> includes, List<Interval> excludes)
            {
                var result = new List<Interval>();
    
                if (!excludes.Any())
                {
                    return includes.Select(x => x.Clone()).ToList();
                }
    
                for (int i = 0; i < includes.Count; i++)
                {
                    for (int j = 0; j < excludes.Count; j++)
                    {
                        if (includes[i].Max < excludes[j].Min || includes[i].Min > excludes[j].Max)
                            continue; // no crossing
    
                        //1 Example: includes[i]=(10-20) excludes[j]=(15-25)
                        if (includes[i].Min < excludes[j].Min && includes[i].Max <= excludes[j].Max)
                        {
                            var interval = new Interval(includes[i].Min, excludes[j].Min - 1);
                            result.Add(interval);
                            break;
                        }
    
                        //2 Example: includes[i]=(10-25) excludes[j]=(15-20)
                        if (includes[i].Min <= excludes[j].Min && includes[i].Max >= excludes[j].Max)
                        {
                            if (includes[i].Min < excludes[j].Min)
                            {
                                var interval1 = new Interval(includes[i].Min, excludes[j].Min - 1);
                                result.Add(interval1);
                            }
                            if (includes[i].Max > excludes[j].Max)
                            {
                                var interval2 = new Interval(excludes[j].Max + 1, includes[i].Max);
                                result.Add(interval2);
                            }
                            break;
                        }
    
                        //3 Example: includes[i]=(15-25) excludes[j]=(10-20)
                        if (includes[i].Min < excludes[j].Max && includes[i].Max > excludes[j].Max)
                        {
                            var interval = new Interval(excludes[j].Max + 1, includes[i].Max);
                            result.Add(interval);
                            break;
                        }
                    }
                }
    
                return result;
            }
    
            /// <summary>
            /// Concatinates intervals if they cross each over
            /// </summary>
            public static List<Interval> ConcatinateIntervals(List<Interval> intervals)
            {
                var result = new List<Interval>();
    
                for (int i = 0; i < intervals.Count; i++)
                {
                    for (int j = 0; j < intervals.Count; j++)
                    {
                        if (i == j)
                            continue;
    
                        if (intervals[i].Max < intervals[j].Min || intervals[i].Min > intervals[j].Max)
                        {
                            Interval interval = intervals[i].Clone();
                            result.Add(interval);
                            continue; // no crossing
                        }
    
                        //1
                        if (intervals[i].Min < intervals[j].Min && intervals[i].Max < intervals[j].Max)
                        {
                            var interval = new Interval(intervals[i].Min, intervals[j].Max);
                            result.Add(interval);
                            break;
                        }
    
                        //2
                        if (intervals[i].Min < intervals[j].Min && intervals[i].Max > intervals[j].Max)
                        {
                            Interval interval = intervals[i].Clone();
                            result.Add(interval);
                            break;
                        }
    
                        //3
                        if (intervals[i].Min < intervals[j].Max && intervals[i].Max > intervals[j].Max)
                        {
                           var interval = new Interval(intervals[j].Min, intervals[i].Max);
                            result.Add(interval);
                            break;
                        }
    
                        //4
                        if (intervals[i].Min > intervals[j].Min && intervals[i].Max < intervals[j].Max)
                        {
                            var interval = new Interval(intervals[j].Min, intervals[j].Max);
                            result.Add(interval);
                            break;
                        }
                    }
                }
    
                return result.Distinct().ToList();
            }
    
            /// <summary>
            /// Parses a source line of intervals to the list of objects
            /// </summary>
            public static List<Interval> ParseIntevals(string intervals)
            {
                var matches = Regex.Matches(intervals, Pattern, RegexOptions.IgnoreCase);
                var list = new List<Interval>();
    
                foreach (Match match in matches)
                {
                    var min = int.Parse(match.Groups[1].Value);
                    var max = int.Parse(match.Groups[2].Value);
                    list.Add(new Interval(min, max));
                }
    
                return list.OrderBy(x => x.Min).ToList();
            }
    
            /// <summary>
            /// Interval
            /// </summary>
            public class Interval
            {
                public int Min { get; set; }
                public int Max { get; set; }
    
                public Interval()
                {
                }
    
                public Interval(int min, int max)
                {
                    Min = min;
                    Max = max;
                }
    
                public override bool Equals(object obj)
                {
                    var obj2 = obj as Interval;
                    if (obj2 == null) return false;
                    return obj2.Min == Min && obj2.Max == Max;
                }
    
                public override int GetHashCode()
                {
                    return this.ToString().GetHashCode();
                }
    
                public override string ToString()
                {
                    return string.Format("{0}-{1}", Min, Max);
                }
    
                public Interval Clone()
                {
                    return (Interval) this.MemberwiseClone();
                }
            }
        }
    }
    

答案 3 :(得分:1)

解决这个问题的方法很多。 LINQ方法尚未讨论 - 我就是这样做的:

// declaring a lambda fn because it's gonna be used by both include/exclude   
// list
Func<string, IEnumerable<int>> rangeFn = 
    baseInput =>
    {
        return baseInput.Split (new []{ ',', ' ' }, 
            StringSplitOptions.RemoveEmptyEntries)
            .SelectMany (rng => 
                {
                    var range = rng.Split (new []{ '-' }, 
                        StringSplitOptions.RemoveEmptyEntries)
                        .Select(i => Convert.ToInt32(i));

                    // just in case someone types in
                    // a reverse range (e.g. 10-5), LOL...
                    var start = range.Min ();
                    var end = range.Max ();

                    return Enumerable.Range (start, (end - start + 1));
                });
        };

var includes = rangeFn (_string);
var excludes = rangeFn (_stringB);

var result = includes.Except (excludes).Distinct().OrderBy(r => r);