I have
var list1 = new List<int> { 1, 2, 3 };
var list2 = new List<int> { 1, 2, 4 };
I want from this:
listAdded: {4}
listRemoved: {2}
The elements are distinct.
How can I:
*Quickly find if list1 and list2 are the same (no changes)
*Get the list of changes (added & removed)?
I'm currently using List<T> but I'm open to HashSet<T> if it will make things faster.
Simply by using Except you can get the differences between two lists in a new collection, then you can check the count of the new collection to see if they are any differences.
var removed = list1.Except(list2).ToList();
var added = list2.Except(list1).ToList();
Then you are free to do a simple if on their Count:
bool areDifferent = removed.Count > 0 || added.Count > 0;
or as Kevin suggested:
bool areDifferent = removed.Any() || added.Any();
Pseudocode with LINQ - (removed - see #meJustAndrew's answer for a better LINQ implementation)
You could do something O(n) if the lists are sorted:
int i = 0;
int j = 0;
while(i < list1.Count && j < list2.Count)
{
if (list1[i] == list2[j])
{
++i;
++j;
}
else if (list1[i] < list2[j])
{
removed.Add(list1[i]);
++i;
}
else // if (list1[i] > list2[j])
{
added.Add(list2[j]);
++j;
}
}
if (i < list1.Count)
{
removed.AddRange(list1.GetRange(i,list1.Count));
}
if (j < list2.Count)
{
added.AddRange(list2.GetRange(j,list2.Count));
}
You can make a Class that derives from List<> and override the Add() and Remove().
Public class MyList<T> : List<T>
{
private List<T> oldItems = new List<T>();
private List<T> newItems = new List<T>();
private List<T> items = new List<T>();
public List<T> Items
{
get { return items; }
set { items = value; }
}
public void Add(T value)
{
Items.Add(value);
newItems.Add(Items.Where(w=>w==value)); // must be the object in the "Items" list
}
public void Remove(T value)
{
Items.Remove(value);
oldItems.Add(value); //value does not exist anymore in `Items`
}
public List<T> GetOldItems()
{
List<T> oldi = oldItems;
oldItems.Clear();
return oldi;
}
public List<T> GetNewItems() //
{
List<T> newi = newItems;
newItems.Clear();
return newi;
}
}
Then you have a list with inside a list for both old items and new items.
When you add a item, it will be registered even so if you remove an item.
When you get the new or old items, the register will be cleared.
Related
I'm trying to make extension methods for generic array, so I could takeout random set of elements.
I made following extension methods for List<T> type and they work great, but I can't work out how to do exactly the same for generic array:
public static T Random<T>(this List<T> list)
{
return list[GameManager.instance.functions.RandomInt(list.Count - 1)];
}
public static IEquatable Random<IEquatable>(this List<IEquatable> list, List<IEquatable> hits)
{
int rand = GameManager.instance.functions.RandomInt(list.Count - 1);
while (hits.Exists(h => h.Equals(list[rand])))
rand = GameManager.instance.functions.RandomInt(list.Count - 1);
return list[rand];
}
public static List<T> Random<T>(this List<T> list, int count)
{
List<T> result = new List<T>();
for (int i = 0; i < count; i++)
{
result.Add(list.Random());
}
return result;
}
public static List<IEquatable> RandomUnique<IEquatable>(this List<IEquatable> list, int count)
{
List<IEquatable> result = new List<IEquatable>();
for (int i = 0; i < count; i++)
{
result.Add(list.Random(result));
}
return result;
}
I tried to rework the first method like this:
public static IEnumerable Random<IEnumerable>(this IEnumerable list)
but it doesn't recognize list as an array so I can't get to it's length value.
I see a workaround, to do a List from Array, then get my random values and make array again, but it's seems like too much action for just taking eg. 2 random from 4 elements array.
Please advise
EDIT:
Thanks to Mathew in comments, I managed to construct the extension method for generic array correctly:
public static T Random<T>(this T[] list)
{
return list[GameManager.instance.functions.RandomInt(list.Length - 1)];
}
But ultimately I'll play around with the Dmitry's answer and try to make these for IEnumerable. Thank you very much!
EDIT2:
Thanks to Zastai, I changed all methods so they work for both List and generic array:
public static T Random<T>(this IReadOnlyList<T> list)
{
return list[GameManager.instance.functions.RandomInt(list.Count - 1)];
}
public static IEquatable Random<IEquatable>(this IReadOnlyList<IEquatable> list, List<IEquatable> hits)
{
int rand = GameManager.instance.functions.RandomInt(list.Count - 1);
while (hits.Exists(h => h.Equals(list[rand])))
rand = GameManager.instance.functions.RandomInt(list.Count - 1);
return list[rand];
}
public static List<T> Random<T>(this IReadOnlyList<T> list, int count)
{
List<T> result = new();
for (int i = 0; i < count; i++)
{
result.Add(list.Random());
}
return result;
}
public static List<IEquatable> RandomUnique<IEquatable>(this IReadOnlyList<IEquatable> list, int count)
{
List<IEquatable> result = new();
for (int i = 0; i < count; i++)
{
result.Add(list.Random(result));
}
return result;
}
Doesn't work for strings (as in "abcdefg".Random()), but for my needs it's not neccessary.
IEnumerable is specifically just a sequence of values, and has no length.
IReadOnlyList on the other hand, is a list of values (so does have a length) and does not allow adding/removing values.
A .NET array implements both.
So if you change your extension methods to take IReadOnlyList<xxx> instead of List<xxx> they should automatically work on arrays too.
Instead of implementing extensions methods for List<T>, T[] etc. you can try implementing a
single routine for IEnumerable<T>, e.g.
public static partial class EnumerableExtensions {
public static T Random<T>(this IEnumerable<T> source) {
//DONE: do not forget to validate public methods' arguments
if (source is null)
throw new ArgumentNullException(nameof(source));
// If enumerable is a collection (array, list) we can address items explictitly
if (source is ICollection<T> collection) {
if (collection.Count <= 0)
throw new ArgumentOutOfRangeException(nameof(source),
$"Empty {nameof(source)} is not supported.");
return collection[GameManager.instance.functions.RandomInt(collection.Count - 1)];
}
// In general case we have to materialize the enumeration
var list = source.ToList();
if (list.Count <= 0)
throw new ArgumentOutOfRangeException(nameof(source),
$"Empty {nameof(source)} is not supported.");
return list[GameManager.instance.functions.RandomInt(list.Count - 1)];
}
}
Then you can use the same extension method with list, array etc.:
// Array
int demo1 = new int[] {4, 5, 6}.Random();
// List
double demo2 = new List<double>() {1.0. 3.0}.Random();
// String is not array or list but implements IEnumerable<char>
char demo3 = "abcdef".Random();
As an alternative to consider: You can use Reservoir sampling to select N items from a sequence of unknown length.
Here's a sample implementation:
/// <summary>Randomly selects n elements from a sequence of items.</summary>
public static List<T> RandomlySelectedItems<T>(IEnumerable<T> items, int n, System.Random rng)
{
// See http://en.wikipedia.org/wiki/Reservoir_sampling for details.
var result = new List<T>(n);
int index = 0;
foreach (var item in items)
{
if (index < n)
{
result.Add(item);
}
else
{
int r = rng.Next(0, index + 1);
if (r < n)
result[r] = item;
}
++index;
}
if (index < n)
throw new ArgumentException("Input sequence too short");
return result;
}
I need 2 lists which are separate and one list containing the items of these two lists.
List<int> list1 = new List<int>();
List<int> list2 = new List<int>();
List<int> list3 = list1 & list2
When I add some integers in list1, I would like them to appear in list3 as well.
I want the same behavior when a new item is added into list2.
A reference of more than one lists.
Is this possible?
No, you can't do that with List<T> directly. However, you could declare:
IEnumerable<int> union = list1.Union(list2);
Now that will be lazily evaluated - every time you iterate over union, it will return every integer which is in either list1 or list2 (or both). It will only return any integer once.
If you want the equivalent but with concatenation, you can use
IEnumerable<int> concatenation = list1.Concat(list2);
Again, that will be lazily evaluated.
As noted in comments, this doesn't expose all the operations that List<T> does, but if you only need to read from the "combined integers" (and do so iteratively rather than in some random access fashion) then it may be all you need.
Is it possible?
Not with List since it's a static data structure - however you could use a query that is the concatenation of the two lists. Then whenever you enumerate the query it will show the current contents:
List<int> list1 = new List<int>();
List<int> list2 = new List<int>();
IEnumerable<int> list3 = list1.Concat(list2);
But as soon as you materialize the query into a data structure (by calling ToList, ToArray, etc.) the contents are static and will not update if one of the underlying lists updates.
No. You can create your own custom type that exposes methods similar to what a List<T> does (an indexer, Add, Remove methods, etc.), possibly even implementing IList<T>, but it wouldn't be a List<T>.
public class CompositeList<T> : IList<T>
{
private IList<IList<T>> lists;
public CompositeList(IList<IList<T>> lists)
{
this.lists = lists;
}
public CompositeList(params IList<T>[] lists)
{
this.lists = lists;
}
public int IndexOf(T item)
{
int globalIndex = 0;
foreach (var list in lists)
{
var localIndex = list.IndexOf(item);
if (localIndex >= 0)
return globalIndex + localIndex;
else
globalIndex += list.Count;
}
return -1;
}
public void Insert(int index, T item)
{
//note that there is an ambiguity over where items should be inserted
//when they are on the border of a set of lists.
foreach (var list in lists)
{
//use greater than or equal instead of just greater than to have the inserted item
//go in the later of the two lists in ambiguous situations,
//rather than the former.
if (index > list.Count)
index -= list.Count;
else
{
list.Insert(index, item);
return;
}
}
//TODO deal with having no lists in `lists`
//TODO deal with having only empty lists in `lists`
throw new IndexOutOfRangeException();
}
public void RemoveAt(int index)
{
foreach (var list in lists)
{
if (index > lists.Count)
index -= list.Count;
else
list.RemoveAt(index);
}
throw new IndexOutOfRangeException();
}
public T this[int index]
{
get
{
foreach (var list in lists)
{
if (index > lists.Count)
index -= list.Count;
else
return list[index];
}
throw new IndexOutOfRangeException();
}
set
{
foreach (var list in lists)
{
if (index > lists.Count)
index -= list.Count;
else
list[index] = value;
}
throw new IndexOutOfRangeException();
}
}
public void Add(T item)
{
if (!lists.Any())
lists.Add(new List<T>());
lists[lists.Count - 1].Add(item);
}
public void Clear()
{
foreach (var list in lists)
list.Clear();
}
public bool Contains(T item)
{
return lists.Any(list => list.Contains(item));
}
public void CopyTo(T[] array, int arrayIndex)
{
if (array.Length - arrayIndex - Count < 0)
throw new ArgumentException("The array is not large enough.");
int iterations = Math.Min(array.Length, Count);
for (int i = arrayIndex; i < iterations; i++)
array[i] = this[i];
}
public int Count
{
get { return lists.Sum(list => list.Count); }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
foreach (var list in lists)
{
if (list.Remove(item))
return true;
}
return false;
}
public IEnumerator<T> GetEnumerator()
{
return lists.SelectMany(x => x).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
I have an unsorted List<WordCount>
class WordCount
{
string word;
int count;
}
And now I must display the top 20 items in descending order of count. How could I code this efficiently? Currently I would set a minimum integer of -1 (all count >= 1) and do a for loop of 20 iterations with a foreach loop inside. This is an issue though because the last few elements in the List could have count of 1 while the top few may have an element with count 1 so now I am stuck on the pseudocode for this implementation for displaying them in order.
I CANNOT use LINQ or any other things other than the methods for List class. I personally think I must accomplish this feat using Sort() and CompareTo() somehow. This is meant to be a brain twister and that is the reason why it has to be done using the given restriction.
This should work:
List<WordCount> counts = new List<WordCount>();
//Fill the list
var result = counts.OrderBy(c => c.Count).Take(20);
Descending order:
var result = counts.OrderByDescending(c => c.Count).Take(20);
[Edit] Using self-made methods:
Here's a solution without any .NET method. First sort the list using an algorithm, in this case I used the Bubblesort (not effeicient for larger collections). Then I take the 20 first element from the sorted result:
public class WordCount
{
public string Word { get; set; }
public int CharCount { get; set; }
}
public List<WordCount> SortList(List<WordCount> list)
{
WordCount temp;
for (int i = list.Count -1; i >= 1; i--)
{
for (int j = 0; j < list.Count -1; j++)
{
if(list[j].CharCount < list[j+1].CharCount)
{
temp = list[j];
list[j] = list[j+1];
list[j+1] = temp;
}
}
}
return list;
}
public List<WordCount> TakeNItems(int n, List<WordCount> list)
{
List<WordCount> temp = new List<WordCount>();
for(int i = 0; i < n; i++)
temp.Add(list[i]);
return temp;
}
//Usage:
var result = SortList(counts);
result = TakeNItems(20, result);
[Edit2] Using Sort() / CompareTo()
Yes, it is also possible using Sort() and CompareTo(). This requieres a couple of changes to your class because when you try to use Sort() now, you'll get an InvalidOperationException. This is because the WordCount class does not implement the IComparable interface. Implementing the interface means you'll have to override the Equals() and GetHashCode() methods and provide your own comparer. Here's a simple implementation based on the List(T).Sort Method:
public class WordCount : IComparable<WordCount>
{
public string Word { get; set; }
public int CharCount { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
WordCount wc = obj as WordCount;
return wc == null ? false : Equals(wc);
}
public int CompareTo(WordCount wc)
{
//Descending
return wc == null ? 1 : wc.CharCount.CompareTo(CharCount);
//Ascending
//return wc == null ? 1 : CharCount.CompareTo(wc.CharCount);
}
public override int GetHashCode()
{
return CharCount;
}
public bool Equals(WordCount wc)
{
return wc == null ? false : CharCount.Equals(wc.CharCount);
}
}
//Usage:
List<WordCount> counts = new List<WordCount>();
//Fill the list
counts.Sort();
And for the limit of 20 items you can write your own extension method which would basically do the same as the Enumerable.Take Method:
public static class Extensions
{
public static IEnumerable<T> TakeN<T>(this List<T> list, int n)
{
for(int i = 0; i < n; i++)
yield return list[i];
}
}
//Usage:
List<WordCount> counts = new List<WordCount>();
//Fill the list with 10000 items and call TakeN()
IEnumerable<WordCount> smallList = counts.TakeN(20);
//Or
counts = counts.TakeN(20).ToList();
Hope this clarifies it all! ;)
The most straight-forward solution, using System.Linq:
var words = new List<WordCount>();
var result = from w in words orderby w.count descending select w.word;
result = result.Take(20);
This is most convenient and clear solution, so when possible use Linq. Also the result will be an IEnumerable<WordCount>, so compiler can do optimizations such as lazy enumeration, not calculating all elements until asked for them.
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’m trying to develop a type to track the current iteration position with a list.
I ideally want to used it with a foreach loop using the IEnumerable interface but the interface has no start/stop events or method to hooking to reset the count.
Currently I have created a GetNext( ) method which return the next value in the list and increments a count by 1.
Does anyone know I can achieve the same functionality using IEnumerable so I can use the type with a foreach loop?
So for example; imagine a list contains 10 items. One method could iterate an instance of the type to position 4 then method two would iterate the same instance starting at position 5 to 6 then method 3 would iterate the remainng from position 7 to 10 – so the type instance tracks the current position.
Any ideas are greatly appreciated (code shown below) . Thanks
public sealed class PositionTracker<T> : IEnumerable
{
private readonly object _syncLock = new object();
private readonly IList<T> _list = new List<T>();
private int _current;
public PositionTracker(IList<T> list)
{
_list = list;
}
public T GetCurrent()
{
lock (_syncLock)
{
return _list[_current];
}
}
public T GetNext()
{
lock (_syncLock)
{
T t = GetCurrent();
if (_current < _list.Count - 1)
{
_current++;
}
return t;
}
}
public IEnumerator<T> GetEnumerator()
{
lock (_syncLock)
{
return _list.GetEnumerator();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Reset()
{
lock (_syncLock)
{
_current = 0;
}
}
public int Count
{
get
{
lock (_syncLock)
{
return _list.Count;
}
}
}
}
[TestFixture]
public class PositionTrackerTests
{
[Test]
public void Position_CurrentPosition_Test()
{
List<string> list = new List<string>(new string[] { "A", "B", "C", "D" });
PositionTracker<string> positionTracker = new PositionTracker<string>(list);
Assert.IsTrue(positionTracker.GetNext().Equals("A"));
Assert.IsTrue(positionTracker.GetNext().Equals("B"));
Assert.IsTrue(positionTracker.GetNext().Equals("C"));
Assert.IsTrue(positionTracker.GetNext().Equals("D"));
}
}
Have a look at yield keyword. Especially this link of Chapter 6 of the Book 'C# in Depth' By Jon Skeet
P.S. I hope you are doing it in C#.NET 2.0+
Check this link: foreach with generic List, detecting first iteration when using value type
There is a link to a SmartEnumerable class by Jon Skeet. It is basically a wrapper for IEnumerable, which gives you a public SmartEnumerable<string>.Entry class which contains the item's index.
Also, nothing stops you from doing this:
public class MyClass
{
private List<String> list = new List<String>() { "1", "2", "3", "4", "5" }
public IEnumerable<String> GetItems(int from, int to)
{
for (int i=from; i<to; i++)
yield return list[i];
}
}
You can achieve most of this with an extension method, and overloads of Enumerable.Select and Where.
Both Select and Where have overloads where the delegate is passed both the item and its index:
var input = new[]{'a','b','c','d'};
var indexed = input.Select((v,i) => new { Value = v, Index = i });
foreach (var v in indexed) {
Console.WriteLine("Index #{0} is '{1}'", v.Index, v.Value);
}
To trigger delegates before the first and after the last items (but only if there is at least one item):
public static IEnumerable<T> StartAndEnd<T>(this IEnumerable<T> input,
Action onFirst,
Action onLast) {
var e = input.GetEnumerator();
if (!e.MoveNext()) { yield break; }
onFirst();
do {
yield return e.Current;
} while (e.MoveNext());
onLast();
}
and then use it as:
var input = new[]{'a','b','c','d'};
var indexed = input.StartAndEnd(() => { Console.WriteLine("First!");},
() => { Console.WriteLine("Last!");})
.Select((v,i) => new { Value = v, Index = i });
foreach (var v in indexed) {
Console.WriteLine("Index #{0} is '{1}'", v.Index, v.Value);
}
which gives the result:
First!
Index #0 is 'a'
Index #1 is 'b'
Index #2 is 'c'
Index #3 is 'd'
Last!
The delegates could set a local (by forming a closure) which is checked in a loop.
A more sophisticated version could call the onLast delegate before the last element of the sequence, but this would require buffering an element of the sequence to detect end before yielding that element.