4

我有一个数字集合(Collection),它可以是任意大小,并且包含负数和正数。我试图根据一些标准将其拆分。从集合中的第一个数字开始,我想在该数字高于 -180 和低于 180 时进行集合。任何高于 180 的数字都将进入新集合,或者任何低于 -180 的数字将进入新集合。如果数字再次进入可接受的参数范围内,那么它们将再次进入新集合。问题是收藏品需要保持井井有条。

例如。

收集 100 个:

  • 前 50 个介于 180 和 -180 之间。
  • 接下来的20个低于-180
  • 接下来的20个都在180以上
  • 最后 10 个在 180 到 -180 之间

从上面的集合中,我现在应该有 4 个单独的集合,其顺序与原始 1 个集合的顺序相同。

  • 原始顺序中的第一个集合编号在 180 和 -180 之间
  • 原序中的第二个收集号低于-180
  • 原序第三集编号180以上
  • 原始顺序的第四个集合编号在 180 和 -180 之间

我做了一个尝试,我所拥有的不起作用,而且是一堆令人讨厌的 if 语句。我不太了解linq,但我认为使用它可能会有更优雅的解决方案。任何人都可以在这里帮助我,向我展示如何创建 linq 语句或建议如何让我的 if 语句工作,如果这是最好的方法。

Collection<Tuple<Collection<double>, int>> collectionOfDataSets = new Collection<Tuple<Collection<double>, int>>();
Collection<double> newDataSet = new Collection<double>();
for (int i = 0; i < dataSet.Count; i++) {
    if (dataSet[i] < 180 && dataSet[i] > -180) {
        newDataSet.Add(dataSet[i]);
    } else {
        Tuple<Collection<double>, int> lastEntry = collectionOfDataSets.LastOrDefault(b => b.Item2 == i--);
        if (lastEntry != null){
            lastEntry.Item1.Add(dataSet[i]);
        }
        double lastInLastCollection = collectionOfDataSets.ElementAtOrDefault(collectionOfDataSets.Count).Item1.Last();
        if (newDataSet.Count > 0 && lastInLastCollection!= dataSet[i]){
            collectionOfDataSets.Add(new Tuple<Collection<double>, int>(newDataSet, i));                        
        }
        newDataSet = new Collection<double>();
    }
}

提前感谢您的任何帮助。

4

6 回答 6

1

你的例子很复杂。我将首先陈述并解决一个更简单的问题,然后使用相同的方法解决您原来的问题。


我想将数字列表拆分为偶数和奇数的连续组。例如,给定列表,2,2,4,3,6,2我会将其分为[2,2,4], [3], [6,2]

这可以通过GroupAdjacentBy方法简洁地完成

> var numbers = new List<int>{2,2,4,3,6,2};
> numbers.GroupAdjacentBy(x => x % 2)
[[2,2,4], [3], [6,2]]

要解决您的问题,只需将上面的奇偶分类函数替换为您的分类函数:

> var points = new List<int>{-180,180};
> var f = new Func<int,int>(x => points.BinarySearch(x));
> var numbers = new List<int>{6,-50,100,190,200,20};
> numbers.GroupAdjacentBy(f)
[[6,-50,100], [190,200], [20]]
于 2013-01-15T23:46:15.260 回答
0

如果您需要在值更改后立即更新集合,为什么不使用属性?就像是

// your original collection
public IList<double> OriginalValues; //= new List<double> { -1000, 5, 7 1000 };

public IList<double> BelowMinus180
{
   get { return OriginalValues.Where(x => x < -180).ToList().AsReadOnly(); }
}

public IList<double> BetweenMinus180And180
{
   get { return OriginalValues.Where(x => x >= -180 && x <= 180).ToList().AsReadOnly(); }
}

public IList<double> Above180
{
   get { return OriginalValues.Where(x => x > 180).ToList().AsReadOnly(); }
}
于 2013-01-15T22:40:54.767 回答
0
public static List<List<T>> PartitionBy<T>(this IEnumerable<T> seq, Func<T, bool> predicate)
{
    bool lastPass = true;
    return seq.Aggregate(new List<List<T>>(), (partitions, item) =>
    {
        bool inc = predicate(item);
        if (inc == lastPass)
        {
            if (partitions.Count == 0)
            {
                partitions.Add(new List<T>());
            }
            partitions.Last().Add(item);
        }
        else
        {
            partitions.Add(new List<T> { item });
        }
        lastPass = inc;
        return partitions;
    });
}

然后你可以使用:

List<List<double>> segments = newDataSet.PartitionBy(d => d > -180 && d < 180);
于 2013-01-15T22:43:03.733 回答
0

这个可能的解决方案使用两遍如何。在第一遍中,我们发现索引发生了变化,在第二遍中,我们进行了实际的分区。首先是确定类别的辅助方法:

    protected int DetermineCategory(double number)
    {
        if (number < 180 && number > -180)
            return 0;
        else if (number < -180)
            return 1;
        else
            return 2;
    }

然后是实际的算法:

    List<int> indices = new List<int>();
    int currentCategory = -1;
    for (int i = 0; i < numbers.Count; i++)
    {
        int newCat = DetermineCategory(numbers[i]);
        if (newCat != currentCategory)
        {
            indices.Add(i);
            currentCategory = newCat;
        }
    }
    List<List<double>> collections = new List<List<double>>(indices.Count);
    for (int i = 1; i < indices.Count; ++i)
        collections.Add(new List<double>(
            numbers.Skip(indices[i - 1]).Take(indices[i] - indices[i - 1])));
于 2013-01-15T22:56:06.960 回答
0

这是基于您提供的新信息的新答案。我希望这一次我会更接近你所需要的

public IEnumerable<IList<double>> GetCollectionOfCollections(IList<double> values, IList<double> boundries)
{
    var ordered = values.OrderBy(x => x).ToList();
    for (int i = 0; i < boundries.Count; i++)
    {
        var collection = ordered.Where(x => x < boundries[i]).ToList();
        if (collection.Count > 0)
        {
            ordered = ordered.Except(collection).ToList();
            yield return collection.ToList();
        }
    }
    if (ordered.Count() > 0)
    {
        yield return ordered;
    }
}
于 2013-01-15T23:33:23.753 回答
0

一种使用 linq 的方法。未经测试,但应该可以工作

var firstSet = dataSet.TakeWhile(x=>x>-180&&x<180);
var totalCount = firstSet.Count();
var secondSet = dataSet.Skip(totalCount).TakeWhile(x=>x<-180);
totalCount+=secondSet.Count();
var thirdSet = dataSet.Skip(totalCount).TakeWhile(x=>x>180);
totalCount += thirdSet.Count();
var fourthSet = dataSet.Skip(totalCount);
于 2013-01-16T00:56:58.197 回答