I'd like to be able to create an array in C# with an arbitrary range of index bounds, e.g., a 16 element array with indices of 100-115.
Native arrays in C# are 0-based, but I'm told (e.g., in Luaan's comment here), that the C# Array class allows arbitrary lower and upper bounds. But in the examples I've seen elements in the Array class are accessed via myArray.GetValue() and myArray.SetValue() instead of conventional array syntax like myArray [ foo ].
Array arr = Array.CreateInstance(typeof(string), new[]{16}, new[]{100});
Console.WriteLine(arr.Length); // 16
arr.SetValue("foo", 100);
Console.WriteLine(arr.GetValue(100)); // foo
Is there any way to make an array with some arbitrary starting index, like [100] that I can access with traditional [ ] syntax in C#?
You could create a class that implements the decorator pattern: just implement the IList interface (wich is also implemented by Array) and do whatever shifting you want on the this [int index] property.
The decorator pattern is described here:
http://www.codeproject.com/Articles/479635/UnderstandingplusandplusImplementingplusDecoratorp
The Array class does not support this, but you can write your own array class with one-based indices:
public class OneBasedArray<T>
{
public T[] InnerArray;
public T this[int i]
{
get { return InnerArray[i-1]; }
set { InnerArray[i-1] = value; }
}
}
And then use it like this:
var myArray = new OneBasedArray<int> { InnerArray = new int[]{ 1, 2, 3, 4, 5 } };
for(int i = 1; i <=5; i++)
{
Console.WriteLine(myArray[i]);
}
This code is only to get the idea, such a class would of course need a nicer interface.
You can only use an array's indexer when it's a 0-indexed array.
You can use an indexer for a custom, non-array, type, and use whatever logic you want for it, such as making it non-zero-indexed, but that's not an option for arrays.
I think Lukas' answer is probably the easiest way to handle this, but if, for some reason, you really wanted to use Array.CreateInstance (not sure why you would - maybe some external library might insist on it?), you could wrap it in a class like this:
public class NonZeroArray<T>
{
private readonly Array array;
public T this[int i]
{
get { return (T)array.GetValue(i); }
set { array.SetValue(value, i); }
}
public NonZeroArray(int length, int lowerBounds = 0)
{
array = Array.CreateInstance(typeof(T), new int[] { length}, new int[] { lowerBounds } );
}
}
You could obviously make this a lot prettier (and easier to work with) by having it implement the rest of IList<T>. And if you really need the native array, you could implement a property with a getter to expose it when needed.
Using it would be simply:
var myNonZeroArray = new NonZeroArray<string>(16,100);
myNonZeroArray[100] = "foo";
Console.WriteLine(myNonZeroArray[100]); // prints "foo"
Here's a custom class that extends IList<T> to provide functionality for starting at a non-zero index:
public class NonZeroList<T> : IList<T>
{
private int startIndex;
private List<T> inner;
public NonZeroList(int startIndex, IEnumerable<T> content)
{
this.startIndex = startIndex;
inner = content.ToList();
}
public NonZeroList(int startIndex)
{
this.startIndex = startIndex;
inner = new List<T>();
}
public T this[int i]
{
get
{
return inner[i - startIndex];
}
set
{
inner[i - startIndex] = value;
}
}
public IEnumerator<T> GetEnumerator()
{
foreach (T i in inner)
yield return i;
yield break;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return inner.GetEnumerator();
}
public int IndexOf(T item)
{
return inner.IndexOf(item) + startIndex;
}
public void Insert(int index, T item)
{
inner.Insert(index - startIndex, item);
}
public void RemoveAt(int index)
{
inner.RemoveAt(index - startIndex);
}
public void Add(T item)
{
inner.Add(item);
}
public void Clear()
{
inner.Clear();
}
public bool Contains(T item)
{
return inner.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
inner.CopyTo(array, arrayIndex);
}
public int Count
{
get { return inner.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
return inner.Remove(item);
}
}
To use it, you initialize it just as you would a normal List<T>, but with the start index specified (e.g. NonZeroList<int> myList = new NonZeroList<int>(20) { 0, 1, 2, 3 };). You can then use it just as you would a normal List<T> or T[].
If you want to use it as an array instead of a list, then you can simply add bounds checking or implement IEnumerable<T> instead of IList<T>, and create utility functions yourself.
I have a collection of Layers where they have names and colors. What I want to do is to sort these first based on colors, then based on their names:
class Layer
{
public string Name {get; set;}
public LayerColor Color {get; set;}
}
enum LayerColor
{
Red,
Blue,
Green
}
Like:
(red) layer2
(red) layer7
(blue) layer0
(blue) layer3
...
I was looking at SortedList but that acts like a Dictionary so doesn't allow for duplicate items.
Also I am using an API where I get the list of Layers by creation order, so I need to get the full list of Layers to sort them the way I want.
Eventually the list of Layers will be binded to a WPF UI where the users will have the ability to add new Layers, so that's why I wanted the internal list to always be sorted as the performance is not important (the number of Layers are less than a thousand).
In the end the Layers I sorted will be accessed via something like this:
class Image
{
public MySortedList<Layer> Layers {get; set;}
}
What's the best way to do this?
A little late to the party, but up for posterity's sake.
in order to optimise separation of concerns, I wrote a wrapper class which keeps a list sorted (and allows duplicates), as below:
public class OrderedList<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
{
#region Fields
readonly List<T> _list;
readonly IComparer<T> _comparer;
#endregion
#region Constructors
OrderedList(List<T> list, IComparer<T> comparer)
{
_list = list;
_comparer = comparer;
}
public OrderedList()
: this(new List<T>(), Comparer<T>.Default)
{
}
public OrderedList(IComparer<T> comparer)
: this(new List<T>(), comparer)
{
}
public OrderedList(IEnumerable<T> collection)
: this(collection, Comparer<T>.Default)
{
}
public OrderedList(IEnumerable<T> collection, IComparer<T> comparer)
: this(new List<T>(collection), comparer)
{
_list.Sort(comparer);
}
public OrderedList(int capacity)
: this(new List<T>(capacity), Comparer<T>.Default)
{
}
public OrderedList(int capacity, IComparer<T> comparer)
: this(new List<T>(capacity), comparer)
{
}
//yet to be implemented
//public void OrderedList(Comparison<T> comparison);
#endregion
#region Properties
public int Capacity { get { return _list.Capacity; } set { _list.Capacity = value; } }
public int Count { get { return _list.Count; } }
object IList.this[int index] { get { return _list[index]; } set { _list[index] = (T)value; } }
public T this[int index] { get { return _list[index]; } set { _list[index] = value; } }
//public bool IsSynchronized { get { return false; } }
bool ICollection.IsSynchronized { get { return false; } }
//public object SyncRoot { get { return _list; } }
object ICollection.SyncRoot { get { return _list; } } //? should return this
bool IList.IsFixedSize { get { return false; } }
bool IList.IsReadOnly { get { return false; } }
bool ICollection<T>.IsReadOnly { get { return false; } }
#endregion
#region Methods
void ICollection<T>.Add(T item)
{
Add(item);
}
/// <summary>
/// Adds a new item to the appropriate index of the SortedList
/// </summary>
/// <param name="item">The item to be removed</param>
/// <returns>The index at which the item was inserted</returns>
public int Add(T item)
{
int index = BinarySearch(item);
if (index < 0)
{
index = ~index;
}
_list.Insert(index, item);
return index;
}
int IList.Add(object item)
{
return Add((T)item);
}
//NOT performance tested against other ways algorithms yet
public void AddRange(IEnumerable<T> collection)
{
var insertList = new List<T>(collection);
if (insertList.Count == 0)
{
return;
}
if (_list.Count == 0)
{
_list.AddRange(collection);
_list.Sort(_comparer);
return;
}
//if we insert backwards, index we are inserting at does not keep incrementing
insertList.Sort(_comparer);
int searchLength = _list.Count;
for (int i=insertList.Count-1;i>=0;i--)
{
T item = insertList[i];
int insertIndex = BinarySearch(0, searchLength, item);
if (insertIndex < 0)
{
insertIndex = ~insertIndex;
}
else
{
while (--insertIndex>=0 && _list[insertIndex].Equals(item)) { }
insertIndex++;
}
if (insertIndex<=0)
{
_list.InsertRange(0, insertList.GetRange(0, i+1 ));
break;
}
searchLength = insertIndex-1;
item = _list[searchLength];
int endInsert = i;
while (--i>=0 && _comparer.Compare(insertList[i], item) > 0) { }
i++;
_list.InsertRange(insertIndex, insertList.GetRange(i, endInsert - i +1));
}
}
public int BinarySearch(T item)
{
return _list.BinarySearch(item, _comparer);
}
public int BinarySearch(int index, int count, T item)
{
return _list.BinarySearch(index,count,item, _comparer);
}
public ReadOnlyCollection<T> AsReadOnly()
{
return _list.AsReadOnly();
}
public void Clear() { _list.Clear(); }
public bool Contains(T item) { return BinarySearch(item) >= 0; }
bool IList.Contains(object item)
{
return Contains((T)item);
}
public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter) { return _list.ConvertAll(converter); }
public void CopyTo(T[] array) { _list.CopyTo(array); }
public void CopyTo(T[] array, int arrayIndex) { _list.CopyTo(array,arrayIndex); }
void ICollection.CopyTo(Array array, int arrayIndex) { _list.CopyTo((T[])array, arrayIndex); }
public void CopyTo(int index, T[] array, int arrayIndex, int count) { _list.CopyTo(index, array, arrayIndex, count); }
public void ForEach(Action<T> action)
{
foreach (T item in _list)
{
action(item);
}
}
IEnumerator IEnumerable.GetEnumerator() { return _list.GetEnumerator(); }
public IEnumerator<T> GetEnumerator() { return _list.GetEnumerator(); }
public List<T> GetRange(int index, int count) { return _list.GetRange(index,count); }
public bool Remove(T item)
{
int index = BinarySearch(item);
if (index < 0)
{
return false;
}
_list.RemoveAt(index);
return true;
}
void IList.Remove(object item)
{
Remove((T)item);
}
public void RemoveAt(int index) { _list.RemoveAt(index); }
public void RemoveRange(int index, int count) { _list.RemoveRange(index, count); }
public T[] ToArray() { return _list.ToArray(); }
public void TrimExcess() { _list.TrimExcess(); }
/// <summary>
/// Find the first index of the given item
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public int IndexOf(T item)
{
int index = BinarySearch(item);
if (index < 0) return -1;
while(--index >= 0 && _list[index].Equals(item)){}
return index+1;
}
int IList.IndexOf(object item)
{
return IndexOf((T)item);
}
/// <summary>
/// Find the last index of the given item
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public int LastIndexOf(T item)
{
int index = BinarySearch(item);
if (index < 0) return -1;
while (++index < _list.Count && _list[index].Equals(item)) { }
return index-1;
}
/// <summary>
/// Return all values within bounds specified
/// </summary>
/// <param name="min">Minimum Bound</param>
/// <param name="max">Maximum Bound</param>
/// <returns>subset of list with values within or equal to bounds specified</returns>
public T[] WithinRange(T min, T max)
{
if (_comparer.Compare(min,max) > 0)
{
throw new ArgumentException("min must be <= max");
}
int minSearchLength;
int maxIndex = _list.BinarySearch(max, _comparer);
if (maxIndex >= 0)
{
minSearchLength = maxIndex + 1;
while (++maxIndex < _list.Count && _comparer.Compare(max, _list[maxIndex]) == 0) { }
--maxIndex;
}
else
{
minSearchLength = ~maxIndex;
if (minSearchLength <= 0)
{
return new T[0];
}
maxIndex = minSearchLength - 1;
}
int minIndex = _list.BinarySearch(0, minSearchLength, min, _comparer);
if (minIndex >= 0)
{
while (--minIndex >= 0 && _comparer.Compare(max, _list[minIndex]) == 0) { }
++minIndex;
}
else
{
minIndex = ~minIndex;
if (minIndex > maxIndex)
{
return new T[0];
}
}
int length = maxIndex - minIndex + 1;
var returnVar = new T[length];
_list.CopyTo(minIndex, returnVar, 0, length);
return returnVar;
}
#endregion
#region NotImplemented
const string _insertExceptionMsg = "SortedList detemines position to insert automatically - use add method without an index";
void IList.Insert(int index, object item)
{
throw new NotImplementedException(_insertExceptionMsg);
}
void IList<T>.Insert(int index, T item)
{
throw new NotImplementedException(_insertExceptionMsg);
}
#endregion
}
Tests written are not extensive (or pretty) but are included in case anyone wanted to expand on them
[TestClass]
public class TestOrderedList
{
[TestMethod]
public void TestIntegerList()
{
var startList = new List<int>(new int[] { 5, 2, 1, 4, 5, 5, 2 });
var olist = new OrderedList<int>(startList);
startList = startList.OrderBy(l => l).ToList();
CollectionAssert.AreEqual(startList, olist);
Assert.AreEqual(0, olist.Add(0));
int nextInc = olist.Max() + 1;
Assert.AreEqual(olist.Count, olist.Add(nextInc));
CollectionAssert.AreEqual(startList.Concat(new int[] { 0, nextInc }).OrderBy(l => l).ToList(), olist);
Assert.IsTrue(olist.Remove(0));
Assert.IsFalse(olist.Remove(0));
Assert.IsTrue(olist.Remove(nextInc));
CollectionAssert.AreEqual(startList, olist);
var addList = new List<int>(new int[] { 5, -1, 2, 2, -1, 3, 2 });
olist.AddRange(addList);
addList = startList.Concat(addList).OrderBy(l => l).ToList();
CollectionAssert.AreEqual(addList, olist);
olist.Remove(-1);
addList.Remove(-1);
CollectionAssert.AreEqual(addList, olist);
olist.Remove(2);
addList.Remove(2);
CollectionAssert.AreEqual(addList, olist);
olist = new OrderedList<int>();
int[] seed = new int[] { -2, -2 };
olist.AddRange(seed);
CollectionAssert.AreEqual(seed, olist);
olist.AddRange(new int[] { });
olist.AddRange(new int[] { -2 });
CollectionAssert.AreEqual(seed.Concat(new int[] { -2 }).ToList(), olist);
olist.AddRange(new int[] { -3 });
CollectionAssert.AreEqual((new int[] { -3, -2 }).Concat(seed).ToList(), olist);
}
[TestMethod]
public void TestIndexOf()
{
var test = new OrderedList<int>(new[] { 0, -1, -2 });
Assert.AreEqual(0, test.IndexOf(-2));
Assert.AreEqual(2, test.IndexOf(0));
test.Add(-2);
Assert.AreEqual(0, test.IndexOf(-2));
Assert.AreEqual(1, test.LastIndexOf(-2));
test.Add(0);
Assert.AreEqual(3, test.IndexOf(0));
Assert.AreEqual(4, test.LastIndexOf(0));
}
[TestMethod]
public void TestRangeFinding()
{
var test = new OrderedList<int> { 2 };
CollectionAssert.AreEqual(new[] { 2 }, test.WithinRange(0, 6));
CollectionAssert.AreEqual(new[] { 2 }, test.WithinRange(0, 2));
CollectionAssert.AreEqual(new[] { 2 }, test.WithinRange(2, 4));
CollectionAssert.AreEqual(new int[0], test.WithinRange(-6, 0));
CollectionAssert.AreEqual(new int[0], test.WithinRange(6, 8));
test = new OrderedList<int>();
CollectionAssert.AreEqual(new int[0], test.WithinRange(6, 8));
test = new OrderedList<int>{ -4, -2, 0 ,4, 6, 6 };
CollectionAssert.AreEqual(new[] { 0, 4 }, test.WithinRange(0, 4));
CollectionAssert.AreEqual(new[] { 0, 4 }, test.WithinRange(-1, 5));
CollectionAssert.AreEqual(new[] { 6, 6 }, test.WithinRange(6, 8));
CollectionAssert.AreEqual(new[] { 6, 6 }, test.WithinRange(5, 8));
CollectionAssert.AreEqual(new[] { -4, -2 }, test.WithinRange(-5, -1));
CollectionAssert.AreEqual(new[] { -4, }, test.WithinRange(-4, -3));
CollectionAssert.AreEqual(new int[0], test.WithinRange(-6, -5));
Assert.ThrowsException<ArgumentException>(() => test.WithinRange(6, 4));
}
}
Did you search for it? Generic SortedList and SortedList.
So I missed the duplicate part which make it a little bit harder I agree. But here is how I would solve it:
var sortedList = new SortedList<LayerColor, SortedList<Layer, Layer>>();
var redSortedList = new SortedList<Layer, Layer>();
// Add all layers associated with the color red
sortedList.Add(LayerColor.Red, redSortedList);
Will that work for you. Also, I would prefer to use linq but if you really want a sorted list my solution will most likely work.
Last try:) :
public class YourClass
{
private List<Layer> _layers;
public List<Layer> Layers
{
get
{
_layers = _layers.OrderBy(y => y.LayerColor).ThenBy(y => y.Name).ToList();
return _layers;
}
set
{
_layers = value;
}
}
}
Note that I'm writing directly in browser without testing it in VS (sitting on OS X), but you probably get the point.
You could use the regular List<T>, but call the Sort() method prior to displaying the list, and after new values are added. That should give you the functionality that you need. The performance will be good enough for this application.
Of course, you will have to define your own comparison for it to use, but that shouldn't be too much trouble.
If you don't have any hooks into the add event that can be used to sort the list, then you can wrap the list in a custom collection class as #Justin recommends.
If the sort is only for display purposes, let WPF handle it:
ICollectionView view = CollectionViewSource.GetDefaultView(Layers);
view.SortDescriptions.Add(new SortDescription("Color", ListSortDirection.Ascending);
view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending);
then just bind Layers to your UI ItemsControl.
You're on the right track. I would create a custom collection class that inherits from Collection. In this custom collection you can override on the on insert/on delete methods and sort your collection as items are added/removed from it.
using System.Linq, do:
from layer in layers
orderby layer.Color, layer.Name
select layer
Start by implementing the IComparable interface in Layer and declaring a CompareTo method. Then use a SortedList collection to store your object.
public class Layer : IComparable {
public int CompareTo(object obj) {
//return -1 if this is before obj, 0 is same, 1 is after.
}
}
You can use arraylist and do below linq query to sort them
ArrayList myList = new ArrayList();
Layer obj1 = new Layer();
obj1.Color = LayerColor.Red;
obj1.Name = "Layer1";
myList.Add(obj1);
Layer obj2 = new Layer();
obj2.Color = LayerColor.Green;
obj2.Name = "Layer2";
myList.Add(obj2);
Layer obj3 = new Layer();
obj3.Color = LayerColor.Blue;
obj3.Name = "Layer3";
myList.Add(obj3);
Layer obj4 = new Layer();
obj4.Color = LayerColor.Green;
obj4.Name = "Layer4";
myList.Add(obj4);
var mySortedList = myList.OfType<Layer>().OrderBy(l => l.Color)
.ThenBy(l => l.Name);
I am working in .Net environment using C#. I need some alternative for the Stack data structure. Some kind of bound stack. The quantity of elements in the collection shouldn't exceed some fixed specified number. And, if that number achieved and new element is pushed, than most older element must be deleted. I need this for storing commands for Undo/Redo strategies.
A circular buffer should do the job; easy enough to wrap a list or array, but nothing built in AFAIK.
Johnny Coder has an implementation here: http://johnnycoder.com/blog/2008/01/07/undo-functionality-with-a-limited-stack/
This is an implementation of a stack with a constrained capacity.
After reaching the given capacity, bottom items of the stack beyond the capacity are discarded. It is possible to iterate through the contained objects and set the index to a specifc position (like a rewind) for discarding multiple entries at once when pushing a new item to the stack.
This is an own implementation with some goodies that prevents you from handling more then one list if you need to go back in the history and forward again (is builtin).
public class DiscardingStack<TObject> : IEnumerable<TObject>
{
private readonly int capacity;
private readonly List<TObject> items;
private int index = -1;
public DiscardingStack(int capacity)
{
this.capacity = capacity;
items = new List<TObject>(capacity);
}
public DiscardingStack(int capacity, IEnumerable<TObject> collection)
: this(capacity)
{
foreach (var o in collection)
{
Push(o);
}
}
public DiscardingStack(ICollection<TObject> collection)
: this(collection.Count, collection)
{
}
public void Clear()
{
if (items.Count >= 0)
{
items.Clear();
index = items.Count - 1;
}
}
public int Index
{
get { return index; }
set
{
if (index >= 0 && index < items.Count)
{
index = value;
}
else throw new InvalidOperationException();
}
}
public int Count
{
get { return items.Count; }
}
public TObject Current
{
get { return items[index]; }
set { index = items.IndexOf(value); }
}
public int Capacity
{
get { return capacity; }
}
public TObject Pop()
{
if (items.Count <= 0)
throw new InvalidOperationException();
var i = items.Count - 1;
var removed = items[i];
items.RemoveAt(i);
if (index > i)
index = i;
return removed;
}
public void Push(TObject item)
{
if (index == capacity - 1)
{
items.RemoveAt(0);
index--;
}
else if (index < items.Count - 1)
{
var removeAt = index + 1;
var removeCount = items.Count - removeAt;
items.RemoveRange(removeAt, removeCount);
}
items.Add(item);
index = items.Count - 1;
}
public TObject Peek()
{
return items[items.Count-1];
}
public TObject this[int i]
{
get { return items[i]; }
}
public IEnumerator<TObject> GetEnumerator()
{
return items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Anyway, building a stack that discards elements when the maximum capacity is reached should be implemented using a LinkedList (as suggested above) if your list is huge (avoids copying). So the LinkedList idea might be better in such a case instead of wrapping a List if the buffer maximum is a high value.
I would also recommend to pack the Push(), Pop() etc. into an interface (e.g. IStack). Sadly, there is no IStack interface predefined in .Net (afaik).
.Net is rather deficient in type of collections. You'll find a collection library here. Use CircularQueue.
There's no builtin Class for this in Framework. (we dont expect to delete data automatically). But you can very well Extend the Stack class and Override Push/Pop and other Methods to suit your needs.
I am using ListView/DataPager.
For performance reasons I page my results at database, using ROW_NUMBER(SQl2005).
At my C# code just comes one page at time. How can I say to DataPager that I have more rows that really are at my List?
I created a class that gerenate fake default(T) objects. Worked fine:
public class PagedList<T> : IEnumerable<T>, ICollection
{
private IEnumerable<T> ActualPage { get; set; }
private int Total { get; set; }
private int StartIndex { get; set; }
public PagedList(int total, int startIndex, IEnumerable<T> actualPage)
{
ActualPage = actualPage;
Total = total;
StartIndex = startIndex;
}
public IEnumerator<T> GetEnumerator()
{
bool passouPagina = false;
for (int i = 0; i < Total; i++)
{
if (i < StartIndex || passouPagina)
{
yield return default(T);
}
else
{
passouPagina = true;
foreach (T itempagina in ActualPage)
{
i++;
yield return itempagina;
}
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#region Implementation of ICollection
void ICollection.CopyTo(Array array, int index)
{
throw new NotSupportedException();
}
public int Count
{
get { return Total; }
}
object ICollection.SyncRoot
{
get { throw new NotSupportedException(); }
}
bool ICollection.IsSynchronized
{
get { throw new NotSupportedException(); }
}
#endregion
}
Usage example:
int totalRows = DB.GetTotalPeople();
int rowIndex = (currentPage-1)*pageSize;
List<Person> peoplePage = DB.GetPeopleAtPage(currentPage);
listview.DataSource = new PagedList(totalRows, rowIndex, peoplePage)
listView.DataBind();
Apparently I can't comment on the above solution, that was provided by Fujiy, however I discovered the following bug:
Inside GetEnumerator() the incrementation in the else branch will always cause the collection to skip one default element, unless you're on the last page of the PagedList.
As an example, if you would create a paged list of 5 elements, with startindex 3 and 1 element per page. This could would enter the else branch for element 2. It would increment i to 3 and then go back into the for-header where it would increment to 4, without creating a default element for i == 3.
i == 1 -> default
i == 2 -> default
i == 3 -> Actual element
i == 4 -> Skipped
i == 5 -> default
A simple solution would be to either use 3 for-loops (one for defaults before the ActualPage, one for the ActualPage and one for elements after the ActualPage). Or to add a i-- after the For-loop inside the Else-branch.
I need to sort some objects according to their contents (in fact according to one of their properties, which is NOT the key and may be duplicated between different objects).
.NET provides two classes (SortedDictionary and SortedList), and both are implemented using a binary tree. The only differences between them are
SortedList uses less memory than SortedDictionary.
SortedDictionary has faster insertion and removal operations for unsorted data, O(log n) as opposed to O(n) for SortedList.
If the list is populated all at once from sorted data, SortedList is faster than SortedDictionary.
I could achieve what I want using a List, and then using its Sort() method with a custom implementation of IComparer, but it would not be time-efficient as I would sort the whole List each time I want to insert a new object, whereas a good SortedList would just insert the item at the right position.
What I need is a SortedList class with a RefreshPosition(int index) to move only the changed (or inserted) object rather than resorting the whole list each time an object inside changes.
Am I missing something obvious ?
Maybe I'm slow, but isn't this the easiest implementation ever?
class SortedList<T> : List<T>
{
public new void Add(T item)
{
Insert(~BinarySearch(item), item);
}
}
http://msdn.microsoft.com/en-us/library/w4e7fxsh.aspx
Unfortunately, Add wasn't overrideable so I had to new it which isn't so nice when you have List<T> list = new SortedList<T>; which I actually needed to do.... so I went ahead and rebuilt the whole thing...
class SortedList<T> : IList<T>
{
private List<T> list = new List<T>();
public int IndexOf(T item)
{
var index = list.BinarySearch(item);
return index < 0 ? -1 : index;
}
public void Insert(int index, T item)
{
throw new NotImplementedException("Cannot insert at index; must preserve order.");
}
public void RemoveAt(int index)
{
list.RemoveAt(index);
}
public T this[int index]
{
get
{
return list[index];
}
set
{
list.RemoveAt(index);
this.Add(value);
}
}
public void Add(T item)
{
list.Insert(~list.BinarySearch(item), item);
}
public void Clear()
{
list.Clear();
}
public bool Contains(T item)
{
return list.BinarySearch(item) >= 0;
}
public void CopyTo(T[] array, int arrayIndex)
{
list.CopyTo(array, arrayIndex);
}
public int Count
{
get { return list.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
var index = list.BinarySearch(item);
if (index < 0) return false;
list.RemoveAt(index);
return true;
}
public IEnumerator<T> GetEnumerator()
{
return list.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return list.GetEnumerator();
}
}
Or perhaps something like this is a more appropriate Remove function...
public bool Remove(T item)
{
var index = list.BinarySearch(item);
if (index < 0) return false;
while (((IComparable)item).CompareTo((IComparable)list[index]) == 0)
{
if (item == list[index])
{
list.RemoveAt(index);
return true;
}
index++;
}
return false;
}
Assuming items can compare equal but not be equal...
I eventually decided to write it :
class RealSortedList<T> : List<T>
{
public IComparer<T> comparer;
public int SortItem(int index)
{
T item = this[index];
this.RemoveAt(index);
int goodposition=FindLocation(this[index], 0, this.Count);
this.Insert(goodposition, item);
return goodposition;
}
public int FindLocation(T item, int begin, int end)
{
if (begin==end)
return begin;
int middle = begin + end / 2;
int comparisonvalue = comparer.Compare(item, this[middle]);
if (comparisonvalue < 0)
return FindLocation(item,begin, middle);
else if (comparisonvalue > 0)
return FindLocation(item,middle, end);
else
return middle;
}
}
Don't forget that inserting an item into a list backed by an array can be an expensive operation - inserting a bunch of items and then sorting may well be quicker unless you really need to sort after every single operation.
Alternatively, you could always wrap a list and make your add operation find the right place and insert it there.
I've solved this problem in the past by writing an extension method that does a binary search on a IList, and another that does an insert. You can look up the correct implementation in the CLR source because there's a built-in version that works only on arrays, and then just tweak it to be an extension on IList.
One of those "should be in the BCL already" things.
What I need is a SortedList class with
a RefreshPosition(int index) to move
only the changed (or inserted) object
rather than resorting the whole list
each time an object inside changes.
Why would you update using an index when such updates invalidate the index? Really, I would think that updating by object reference would be more convenient. You can do this with the SortedList - just remember that your Key type is the same as the return type of the function that extracts the comparable data form the object.
class UpdateableSortedList<K,V> {
private SortedList<K,V> list = new SortedList<K,V>();
public delegate K ExtractKeyFunc(V v);
private ExtractKeyFunc func;
public UpdateableSortedList(ExtractKeyFunc f) { func = f; }
public void Add(V v) {
list[func(v)] = v;
}
public void Update(V v) {
int i = list.IndexOfValue(v);
if (i >= 0) {
list.RemoveAt(i);
}
list[func(v)] = v;
}
public IEnumerable<T> Values { get { return list.Values; } }
}
Something like that I guess.