2011-06-03 73 views
1

集合的正确顺序我有一个简单的域对象:维护序

class FavoriteFood 
{ 
    public string Name; 
    public int Ordinal; 
} 

我想有保持正确的顺序此域对象的集合。例如,假设4克最喜欢的食物:

Name: Banana, Ordinal: 1 
Name: Orange, Ordinal: 2 
Name: Pear, Ordinal: 3 
Name: Watermelon, Ordinal: 4 

如果我改变梨的顺序,以4应改变西瓜的序数下降到3

如果我添加一个新的最喜爱的食物(草莓)与序号3它应该转移梨最多4和西瓜高达5

如果我改变梨的序号为2应改变橙色至3。

如果我改变西瓜的序号为1,香蕉会一下子提高到2,橙会涨到3,梨会涨到4.

完成此操作的最佳方法是什么?

UPDATE:域对象的名称属性是动态的,并基于用户输入。该对象必须具有此序号属性,因为用户可以更改其最喜欢的食物的显示顺序。这个序号值保存在数据库中,当填充结构时,我不能保证这些项目是按照他们的序号添加的。

我遇到的麻烦是当底层域对象发生变化时,没有更新列表中其余项的好方法。例如:

var favoriteFoods = new List<FavoriteFood>(); 
var banana = new FavoriteFood { Name = "Banana", Ordinal = 1}; 
favoriteFoods.Add(banana); 
favoriteFoods.Add(new FavoriteFood { Name = "Orange", Ordinal = 2 }); 
banana.Ordinal = 2; 
// at this point both Banana and Orange have the same ordinal in the list. How can we make sure that Orange's ordinal gets updated too? 

到目前为止,我已经尝试过做它的工作原理如下:

class FavoriteFood : INotifyPropertyChanging 
{ 
    public string Name; 
    public int Ordinal 
    { 
     get { return this.ordinal; } 
     set 
     { 
      var oldValue = this.ordinal; 
      if (oldValue != value && this.PropertyChanging != null) 
      { 
       this.PropertyChanging(new FavoriteFoodChangingObject { NewOrdinal = value, OldOrdinal = oldValue }, new PropertyChangingEventArgs("Ordinal")); 
      } 
      this.ordinal = value; 
     } 
    } 

    internal struct FavoriteFoodChangingObject 
    { 
     internal int NewOrdinal; 
     internal int OldOrdinal; 
    } 

    // THIS IS A TEMPORARY WORKAROUND 
    internal int ordinal; 

    public event PropertyChangingEventHandler PropertyChanging; 
} 

public class FavoriteFoodCollection : IEnumerable<FavoriteFood> 
{ 
    private class FavoriteFoodOrdinalComparer : IComparer<FavoriteFood> 
    { 
     public int Compare(FavoriteFood x, FavoriteFood y) 
     { 
      return x.Ordinal.CompareTo(y.Ordinal); 
     } 
    } 

    private readonly SortedSet<FavoriteFood> underlyingList = new SortedSet<FavoriteFood>(new FavoriteFoodOrdinalComparer()); 

    public IEnumerator<FavoriteFood> GetEnumerator() 
    { 
     return this.underlyingList.GetEnumerator(); 
    } 

    public void AddRange(IEnumerable<FavoriteFood> items) 
    { 
     foreach (var i in items) 
     { 
      this.underlyingList.Add(i); 
     } 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 

    private void UpdateOrdinalsDueToRemoving(FavoriteFood item) 
    { 

     foreach (var i in this.underlyingList.Where(x => x.Ordinal > item.Ordinal)) 
     { 
      i.ordinal--; 
     } 
    } 

    public void Remove(FavoriteFood item) 
    { 
     this.underlyingList.Remove(item); 
     this.UpdateOrdinalsDueToRemoving(item); 
    } 

    public void Add(FavoriteFood item) 
    { 
     this.UpdateOrdinalsDueToAdding(item); 
     this.underlyingList.Add(item); 
     item.PropertyChanging += this.item_PropertyChanging; 
    } 

    private void item_PropertyChanging(object sender, PropertyChangingEventArgs e) 
    { 
     if (e.PropertyName.Equals("Ordinal")) 
     { 
      var ordinalsChanging = (FavoriteFood.FavoriteFoodChangingObject)sender; 
      this.UpdateOrdinalsDueToEditing(ordinalsChanging.NewOrdinal, ordinalsChanging.OldOrdinal); 
     } 
    } 

    private void UpdateOrdinalsDueToEditing(int newOrdinal, int oldOrdinal) 
    { 

     if (newOrdinal > oldOrdinal) 
     { 

      foreach (var i in this.underlyingList.Where(x => x.Ordinal <= newOrdinal && x.Ordinal > oldOrdinal)) 
      { 
       //i.Ordinal = i.Ordinal - 1; 
       i.ordinal--; 
      } 

     } 
     else if (newOrdinal < oldOrdinal) 
     { 

      foreach (var i in this.underlyingList.Where(x => x.Ordinal >= newOrdinal && x.Ordinal < oldOrdinal)) 
      { 
       //i.Ordinal = i.Ordinal + 1; 
       i.ordinal++; 
      } 
     } 
    } 

    private void UpdateOrdinalsDueToAdding(FavoriteFood item) 
    { 

     foreach (var i in this.underlyingList.Where(x => x.Ordinal >= item.Ordinal)) 
     { 
      i.ordinal++; 
     } 
    } 
} 

这工作好了,但使用内部有序场的是一个奇怪的解决方法。这是需要的,以便PropertyChangingEvent不会无限增加。

+2

你有什么试过?什么没有用?你最擅长的是什么?大多数可读?最快的?还有别的吗? – Oded 2011-06-03 20:08:00

回答

0

听起来像是你想要一个SortedList。使用Ordinal作为关键字添加每个项目。

+0

然后插入一个项目... – 2011-06-03 20:11:04

3

只需使用一个List<string>

List<string> foods = new List<string> { "Banana", "Orange", "Pear" }; 
int ordinalOfOrange = foods.IndexOf("Orange"); 

这不是一个好主意,“存储”这个序号,如果它有可能改变你所描述的方式。

+0

序号来自数据库,当填充结构时,我无法保证项目将按照其序号添加。 – smoak 2011-06-03 20:27:14

+0

@smoak:所以使用'.OrderBy(o => o.Ordinal).ToList()'创建列表' – 2011-06-03 20:50:10

+0

@smoak:那么我的代码是一个简化,你可能将不得不存储序号,并更新它删除/插入后的循环。我希望你在那一栏没有任何关系。 – 2011-06-03 21:29:10

0

我会做类似如下:

public class FavoriteFoods 
{ 
    StringComparer comparer ; 
    List<string> list ; 

    public FavoriteFoods() 
    { 
    this.list = new List<string>() ; 
    this.comparer = StringComparer.InvariantCultureIgnoreCase ; 
    return ; 
    } 

    public void Add(string food , int rank) 
    { 
    if (this.list.Contains(food,comparer)) throw new ArgumentException("food") ; 
    this.list.Insert(rank,food) ; 
    return ; 
    } 

    public void Remove(string food) 
    { 
    this.list.Remove(food) ; 
    return ; 
    } 

    public void ChangeRank(string food , int newRank) 
    { 
    int currentRank = this.list.IndexOf(food) ; 

    if (currentRank < 0) throw new ArgumentOutOfRangeException("food") ; 
    if (newRank  < 0    ) throw new ArgumentOutOfRangeException("newRank") ; 
    if (newRank  >= this.list.Count) throw new ArgumentOutOfRangeException("newRank") ; 

    if (newRank != currentRank) 
    { 
     this.Remove(food) ; 
     this.Add(food , newRank) ; 
    } 
    return ; 
    } 

    public int GetRank(string food) 
    { 
    int rank = this.list.IndexOf(food) ; 
    if (rank < 0) throw new ArgumentOutOfRangeException("food"); 
    return rank ; 
    } 

    public IEnumerable<string> InRankOrder() 
    { 
    foreach (string food in this.list) 
    { 
     yield return food ; 
    } 
    } 

} 
0

让我重申你的问题。

你有一个字符串的集合。你有一系列的序号。

您希望能够快速查找字符串的序号。和一串序数。你也希望能够插入一个给定序号的字符串。并改变字符串的序数。

有两种方法可以去。第一个简单的方法是按顺序存储字符串集合,并知道它们的序号。您可以及时扫描列表O(n)。您也可以每次查找,插入,移动和删除O(n)。如果你真的不关心表现,那么我强烈建议这样做。

如果您关心性能,那么您需要构建自定义数据结构。最简单的想法是有两棵树。一棵树以字母顺序存储字符串,并告诉你字符串在另一棵树中的位置。另一棵树按顺序存储字符串,并存储多少东西在各种子树中的计数。

现在,这里是您的基本操作。

  • 插入。在正确的位置插入第二个树(如果您选择移动过程中的其他任何东西,请在第一个树中更新这些东西),然后将该字符串插入第一个树中。
  • 按字符串查找。搜索第一棵树,找到它在第二棵树中的位置,在第二棵树中找回它的序号。
  • 按序号查找。搜索第二个树,找到字符串。
  • 删除。从两棵树中删除。
  • 移动序号。从旧位置的第二棵树上移除。插入新位置的第二棵树。更新第一个树中的所有适当节点。

对于简单的版本,你可以使用树木。如果你想变得有趣,你可以查看B树,红黑树和其他类型的自平衡树,然后选择其中的一种。

如果你正确编程,你可以保证所有的操作需要时间O(log(n))。然而,会有很多不变的开销,对于小型集合来说,聪明的努力可能是相对于简单方法而言的一种损失。