Displaying unsorted List's top 20 items - c#

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.

Related

c# extension methods for generic array

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;
}

Find elements added and removed from 2 lists

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.

How to keep one of duplicate entries in list<T>

I have a list and in the list there are multiple entries. If the list contains an entry that is duplicated then I want to only keep one of the duplicates.
I've tried many things, the list.Distinct().ToList() and this does not remove the duplicate entry, I do not want to override the classes Equals method, so is there a way outside of that.
I've also done this method which seems to again, not remove the duplicate entry as it does not consider object a == object b.
private void removeDupes(List<Bookings> list)
{
int duplicates = 0;
int previousIndex = 0;
for (int i = 0; i < list.Count; i++)
{
bool duplicateFound = false;
for (int x = 0; x < i; x++)
{
if (list[i] == list[x])
{
duplicateFound = true;
duplicates++;
break;
}
}
if (duplicateFound == false)
{
list[previousIndex] = list[i];
previousIndex++;
}
}
}
There is another overload of the Distinct LINQ extension method that also takes an IEqualityComparer as an argument (see this link). So you'd need to create a class that implements IEqualityComparer<Bookings> and supply an instance of it to the Distinct-method. This way, you do not need to override the Equals method of the type.
The rules on whether two objects are equal to one another are implemented in the EqualityComparer.
As an alternative, you can use a HashSet and supply the EqualityComparer in the constructor.
A possible solution for your problem in order of Markus answer might look like this:
public class Booking
{
public Booking(int id, float amount)
{
BookingId = id;
BookingAmount = amount;
}
public int BookingId { get; }
public float BookingAmount { get; }
}
public class BookingComparer : IEqualityComparer<Booking>
{
public bool Equals(Booking x, Booking y)
{
return (x.BookingAmount == y.BookingAmount) && (x.BookingId == y.BookingId);
}
public int GetHashCode(Booking obj)
{
return obj.BookingId.GetHashCode()*17 + obj.BookingAmount.GetHashCode()*17;
}
}
internal class Program
{
private static void Main(string[] args)
{
var booking1 = new Booking(1, 12);
var booking2 = new Booking(1, 12);
var bookings = new List<Booking>();
bookings.Add(booking1);
bookings.Add(booking2);
var result = bookings.Distinct(new BookingComparer()).ToList();
}
}

What is the name of this sorting algorithm in c#

public static int[] Sort(int[] ints)
{
var dictionary = new IndexedDictionary<int, int>();
foreach (var i in ints)
{
dictionary.Add(i, i);
}
for (int i = 0; i <= ints.Length - 1; i++)
{
var indexValue = dictionary[i].Key;
dictionary[indexValue - 1].Value = indexValue;
}
return dictionary.Values();
}
Is it a bucket sort? I've seen a few bucket sorts and they look more complicated than this. Also please ignore the IndexedDictionary class - its a custom class to allow for getting values by index.
Edit: CompuChip - if you want to see the IndexedDictionary:
public class IndexedDictionary<T, TY>
{
public class DicObject<T, Y>
{
public T Key { get; set; }
public Y Value { get; set; }
}
private HashSet<DicObject<T, TY>> list = new HashSet<DicObject<T, TY>>();
public void Add(T o, TY u)
{
list.Add(new DicObject<T, TY>{Key = o, Value = u});
}
public DicObject<T, TY> this[int i] {
get{return list.ElementAt(i);}
}
public T[] Keys()
{
return list.Select(x => x.Key).ToArray();
}
public TY[] Values()
{
return list.Select(x => x.Value).ToArray();
}
}
Its not the crux at all.
That is a form of pigeon hole sorting, but it's limited to only being capable of sorting collections with unique values ranging from 1 and up without gaps. (If you for example try to sort the array {4,3,2} it will crash.)
So, for any collection that it works for, it does the same thing as:
public static int[] Sort(int[] ints) {
return Enumerable.Range(1, ints.Length).ToArray();
}
The IndexedDictionary is only a complicated and slow way to iterate through the items in an arbitrary order and put each value in place according to the same arbitrary order so that they end up being sorted. You can do the same thing much simpler and faster with a regular array:
public static int[] Sort(int[] ints) {
int[] result = new int[ints.Length];
foreach (int value in ints) {
result[value - 1] = value;
}
return result;
}
For an implementation that works with collections that doesn't have to have a lowest value of 1 and be continuous, you would create an array that spans from the lowest to the highest value and put the values in it, then collect them in order:
public static int[] Sort(int[] ints) {
int min = ints.Min();
int max = ints.Max();
int?[] pigeonHoles = new int?[max - min + 1];
foreach (int value in ints) {
pigeonHoles[value - min] = value;
}
int[] result = new int[ints.Length];
int index = 0;
foreach (int? value in pigeonHoles) {
if (value.HasValue) {
result[index++] = value.Value;
}
}
return result;
}
I'm not sure how we can ignore the IndexedDictionary as it seems to be the crux of your algorithm, but assuming it works like a standard dictionary I would think this is basically an insert sort - you put all your values into a dictionary which inserts it in the right place to keep its keys sorted and in the end you just extract all the values which are in the proper order by then. However, all the hard work is invisible because it is inside the Dictionary class.
A similar solution in terms of standard classes would be something like
public static IEnumerable<int> Sort(int[] ints)
{
var dictionary = new Dictionary<int, bool>();
foreach (var i in ints)
{
dictionary.Add(i, false);
}
return dictionary.Keys;
}
(although this does not deal with numbers occurring more than once).

How to create dynamic incrementing variable using “for” loop in C#

How to create dynamic incrementing variable using "for" loop in C#? like this:
track_1, track_2, track_3, track_4. so on.
You can't create dynamically-named variables. All you can do - it to create some collection or array, and operate with it.
I think the best class for you is generic List<>:
List<String> listWithDynamic = new List<String>();
for (int i = 1; i < limit; i +=1)
{
listWithDynamic.Add(string.Format("track_{0}", i));
...
}
Assuming you want strings:
for (int i = 1; i < limit; i +=1)
{
string track = string.Format("track_{0}", i);
...
}
But when you already have variables called track_1, track_2, track_3, track_4 you will need an array or List:
var tracks = new TrackType[] { track_1, track_2, track_3, track_4 } ;
for (int i = 0; i < tracks.length; i++)
{
var track = tracks[i]; // tracks[0] == track_1
...
}
Obvious Solution
for (var i = 0; i < 10; i++)
{
var track = string.Format("track_{0}", i);
}
Linq-Based Solution
foreach (var track in Enumerable.Range(0, 100).Select(x => string.Format("track_{0}", x)))
{
}
Operator-Based Solution This is somewhat hacky, but fun none-the-less.
for (var i = new Frob(0, "track_{0}"); i < 100; i++)
{
Console.WriteLine(i.ValueDescription);
}
struct Frob
{
public int Value { get; private set; }
public string ValueDescription { get; private set; }
private string _format;
public Frob(int value, string format)
: this()
{
Value = value;
ValueDescription = string.Format(format, value);
_format = format;
}
public static Frob operator ++(Frob value)
{
return new Frob(value.Value + 1, value._format);
}
public static Frob operator --(Frob value)
{
return new Frob(value.Value - 1, value._format);
}
public static implicit operator int(Frob value)
{
return value.Value;
}
public static implicit operator string(Frob value)
{
return value.ValueDescription;
}
public override bool Equals(object obj)
{
if (obj is Frob)
{
return ((Frob)obj).Value == Value;
}
else if (obj is string)
{
return ((string)obj) == ValueDescription;
}
else if (obj is int)
{
return ((int)obj) == Value;
}
else
{
return base.Equals(obj);
}
}
public override int GetHashCode()
{
return Value;
}
public override string ToString()
{
return ValueDescription;
}
}
don't know if I get your question, but I will try:
for(var i = 1; i < yourExclusiveUpperbound; i++)
{
var track = String.Format("$track_{0}", i);
// use track
}
or with some LINQ-Magic:
foreach(var track in Enumerate.Range(1, count)
.Select(i => String.Format("$track_{0}", i)))
{
// use track
}
Do as follow:
for (int i = 0; i < lenght; i ++)
{
any work do in loop
}
No, we can't create dynamically named variables in a loop. But, there are other elegant ways to address the problem instead of creating dynamically named variables.
One could be, create an array or list before the loop and store values in array / list items in the loop. You can access the array / list later anywhere in your code. If you know which variable you want to use (track_1, track_2, ...), you can simply access it from the array / list (tracks[1], tracks[2], ...).
List<String> tracks = new List<String>();
for (int i = 1; i < limit; i++)
{
Track track = new Track();
tracks.Add(track);
...
}

Categories

Resources