Resizing a List to fit a new Length - c#

Suppose you have a list defined as:
List<string> list1 = new List<string>{"Cat","Dog","Bird","Badger"};
Now, suppose you wanted to write a generic function that could take that list and change the length of it. So if you're subtracting the length to make the list smaller, it would truncate the items within the list, or if the length is longer, it would copy the contents of the List and then add new additional indices to it.
A bigger length parameter would result in:
List<string> list1 = new List<string>{"Cat","Dog","Bird","Badger","","","","",""};
while a smaller length would result in:
List<string> list1 = new List<string>{"Cat","Dog"};
Assuming that no deep copying needed to be done, how could a function like this be written?

You can use AddRange and RemoveRange, which is better than looping with Add and/or RemoveAt:
public static void PaddedResize<T>(
List<T> list,
int size,
T padding = default(T)
) {
// Compute difference between actual size and desired size
var deltaSize = list.Count - size;
if (deltaSize < 0) {
// If the list is smaller than target size, fill with `padding`
list.AddRange(Enumerable.Repeat(padding, -deltaSize));
} else {
// If the list is larger than target size, remove end of list
list.RemoveRange(size, deltaSize);
}
}
This works for immutable, referentially transparent, or struct types, but for the garden variety class, it will pad the list with the same element over and over, which might be non-ideal. To fix that, take a factory function that creates the padding instead:
public static void PaddedResize<T>(
List<T> list,
int size,
Func<T> paddingFactory // Changed parameter to a factory function
) {
// Compute difference between actual size and desired size
var deltaSize = list.Count - size;
if (deltaSize < 0) {
// If the list is smaller than target size, fill with the result of calling `paddingFactory` for each new item
list.AddRange(Enumerable.Repeat(0, -deltaSize).Select(_ => paddingFactory()));
} else {
// If the list is larger than target size, remove end of list
list.RemoveRange(size, deltaSize);
}
}

You may want to create some ListUtils class and write static generic functions there. In case you want a new list containing needed items
static class Listutils
{
public static List<T> Shrink<T>(List<T> src, Predicate<T> predicate)
{
if (src == null || src.Count == 0)
return null;
List<T> newList = new List<T>();
foreach(T item in src)
{
if (predicate(item))
newList.Add(item);
}
return newList;
}
}
........somewhere in your program
List<string> list = new List<string>{"Cat","Dog","Bird","Badger","Snake"};
// select only Cat, Dog and Bird
List<string> shrinked = ListUtils.Shrink(list, (name) => delegate
{
return (name == "Cat" || name == "Dog" || name == "Bird");
});

Related

Generic list comparison in C#

I have a method that finds differences between two lists of ints using a dictionary. Essentially the code loops the first list, adding each int to the dictionary and setting (to 1 where not already present)/incrementing the value. It then loops the second list setting (to -1 where not already present)/decrementing the value.
Once it has looped both lists you end up with a dictionary where keys with values = 0 indicate a match, keys with values >=1 indicate presence only in the first list and values <=-1 indicate presence only in the second list.
Firstly, is this a sensible implementation?
Secondly, I would like to make it more generic, at the moment it can only handle int based lists. I'd like something that could handle any object where the caller could potentially define the comparison logic...
public static Dictionary<int, int> CompareLists(List<int> listA, List<int> listB)
{
// 0 Match
// <= -1 listB only
// >= 1 listA only
var recTable = new Dictionary<int, int>();
foreach (int value in listA)
{
if (recTable.ContainsKey(value))
recTable[value]++;
else
recTable[value] = 1;
}
foreach (int value in listB)
{
if (recTable.ContainsKey(value))
recTable[value]--;
else
recTable[value] = -1;
}
return recTable;
}
Thanks in advance!
In response to:
"It won't work properly if to example you have same value appears twice in listA and once in listB, result will be positive, which say "listA only" in your comments."
Let me clarify; if a value appears twice in listA it should also appear twice in listB - So if a value is in listA twice and once in listB, I don't care which one from listA it picks to match, as long as the one non-reconciling item is reported correctly.
Imagine the use-case where you are trying to reconcile lots of payment amounts between two files, it's entirely feasible to have repeating amounts but it doesn't really matter which of the duplicates are matched as long as the non-reconciling values are reported.
To answer your second question, here's how to make it more generic:
public static Dictionary<T, int> CompareLists<T>(IEnumerable<T> listA,
IEnumerable<T> listB, IEqualityComparer<T> comp)
{
var recTable = new Dictionary<T, int>(comp);
foreach (var value in listA)
{
if (recTable.ContainsKey(value))
recTable[value]++;
else
recTable[value] = 1;
}
foreach (var value in listB)
{
if (recTable.ContainsKey(value))
recTable[value]--;
else
recTable[value] = -1;
}
return recTable;
}
This is more generic because:
I pass in the type T instead of an int.
I use IEnumerables instead of Lists.
I pass in an IEqualityComparer and pass it to the Dictionary constructor which needs to use it.
I use var in the foreach loops instead of int. You can also use T.
You call this code like this:
static void Main()
{
int[] arr1 = { 1, 2, 3 };
int[] arr2 = { 3, 2, 1 };
var obj = CompareLists(arr1, arr2, EqualityComparer<int>.Default);
Console.ReadLine();
}
Here's an example of implementing IEqualityComparer. This treats all odd ints as equal and all even ints as equal:
public class MyEq : IEqualityComparer<int>
{
public bool Equals(int x, int y)
{
return (x % 2) == (y % 2);
}
public int GetHashCode(int obj)
{
return (obj % 2).GetHashCode();
}
}
FullOuterJoin as found here: LINQ - Full Outer Join
public static Dictionary<int, int> CompareLists(List<int> listA, List<int> listB)
{
return listA.FullOuterJoin(listB,
a=>a, // What to compare from ListA
b=>b, // What to compare from ListB
(a,b,key)=>
new {key=key,value=0}, // What to return if found in both
new {key=key,value=-1},// What to return if found only in A
new {key=key,value=1}) // What to return if found only in B
.ToDictionary(a=>a.key,a=>a.value); // Only because you want a dictionary
}
You can do this using Generics:
public static Dictionary<T, int> CompareLists<T>(List<T> listA, List<T> listB)
{
// 0 Match
// <= -1 listB only
// >= 1 listA only
var recTable = new Dictionary<T, int>();
foreach (T value in listA)
{
if (recTable.ContainsKey(value))
recTable[value]++;
else
recTable[value] = 1;
}
foreach (T value in listB)
{
if (recTable.ContainsKey(value))
recTable[value]--;
else
recTable[value] = -1;
}
return recTable;
}
These are my two cents:
public static Dictionary<T, int> CompareLists<T>(List<T> left, List<T> right, IEqualityComparer<T> comparer)
{
Dictionary<T, int> result = left.ToDictionary(l => l, l => right.Any(r => comparer.Equals(l, r)) ? 0 : -1);
foreach (T r in right.Where(t => result.Keys.All(k => !comparer.Equals(k, t))))
result[r] = 1;
return result;
}
The method takes Lists of any type T and an IEqualityComparer for that type T. It then at first generates a dictionary of those elements contained in the "left" List, thereby checking if they are also in the "right" List and setting the value accordingly.
The second step adds the elements that are only contained in the "right" List with value 1.
If this is a sensible implementation depends on what you are trying to achieve with it. I think it's a short but still readable one, relying on proper implementation of the LINQ methods. Though there might be faster possibilities one could think about if this is for really big lists or an very often called method.

What is the best way to trim a list?

I have a List of strings. Its being generated elsewhere but i will generate it below to help describe this simplified example
var list = new List<string>();
list.Add("Joe");
list.Add("");
list.Add("Bill");
list.Add("Bill");
list.Add("");
list.Add("Scott");
list.Add("Joe");
list.Add("");
list.Add("");
list = TrimList(list);
I would like a function that "trims" this list and by trim I want to remove all items at the end of the array that are blank strings (the final two in this case).
NOTE: I still want to keep the blank one that is the second item in the array (or any other one that is just not at the end) so I can't do a .Where(r=> String.isNullOrEmpty(r))
I would just write it without any LINQ, to be honest- after all, you're modifying a collection rather than just querying it:
void TrimList(List<string> list)
{
int lastNonEmpty = list.FindLastIndex(x => !string.IsNullOrEmpty(x));
int firstToRemove = lastNonEmpty + 1;
list.RemoveRange(firstToRemove, list.Count - firstToRemove);
}
If you actually want to create a new list, then the LINQ-based solutions are okay... although potentially somewhat inefficient (as Reverse has to buffer everything).
Take advantage of Reverse and SkipWhile.
list = list.Reverse().SkipWhile(s => String.IsNullOrEmpty(s)).Reverse().ToList();
List<T> (not the interface) has a FindLastIndex method. Therefore you can wrap that in a method:
static IList<string> TrimList(List<string> input) {
return input.Take(input.FindLastIndex(x => !string.IsNullOrEmpty(x)) + 1)
.ToList();
}
This produces a copy, whereas Jon's modifies the list.
The only solution I can think of is to code a loop that starts at the end of the list and searches for an element that is not an empty string. Don't know of any library functions that would help. Once you know the last good element, you know which ones to remove.
Be careful not to modify the collection while you are iterating over it. Tends to break the iterator.
I always like to come up with the most generic solution possible. Why restrict yourself with lists and strings? Let's make an algorithm for generic enumerable!
public static class EnumerableExtensions
{
public static IEnumerable<T> TrimEnd<T>(this IEnumerable<T> enumerable, Predicate<T> predicate)
{
if (predicate == null)
{
throw new ArgumentNullException("predicate");
}
var accumulator = new LinkedList<T>();
foreach (var item in enumerable)
{
if (predicate(item))
{
accumulator.AddLast(item);
}
else
{
foreach (var accumulated in accumulator)
{
yield return accumulated;
}
accumulator.Clear();
yield return item;
}
}
}
}
Use it like this:
var list = new[]
{
"Joe",
"",
"Bill",
"Bill",
"",
"Scott",
"Joe",
"",
""
};
foreach (var item in list.TrimEnd(string.IsNullOrEmpty))
{
Console.WriteLine(item);
}

How to "Dequeue" Element from a List?

I have a List of cards called _deck:
private List<String> _deck = new List<String> {"2h", "3h", "4h", ... }
And then I want to remove a card from the List and save into a variable. I'm trying to do:
String p1FirstCard = _deck.RemoveAt(0);
but I'm getting the error
Cannot convert type void to String
In C# List is there something like push/pop but which does that at the "head" or "start" of the List? (Push/pop works at the "tail" or "end" of the list.)
If not, how should I do remove the first element but save it in a variable?
If you want to dequeue the first element, you could simply use a Queue<T>.
class Program
{
static void Main(string[] args)
{
var _deck = new Queue<String>();
_deck.Enqueue("2h");
_deck.Enqueue("3h");
_deck.Enqueue("4h");
_deck.Enqueue("...");
var first = _deck.Dequeue(); // 2h
first = _deck.Dequeue(); // 3h
}
}
If you want to pop the last element, you could use a Stack<T>.
class Program
{
static void Main(string[] args)
{
var _deck = new Stack<String>();
_deck.Push("2h");
_deck.Push("3h");
_deck.Push("4h");
_deck.Push("...");
var first = _deck.Pop(); // ...
first = _deck.Pop(); // 4h
}
}
You can do it in two steps:
String p1FirstCard = _deck[0];
_deck.RemoveAt(0);
You can write your own extension helper method (I added an index to Pop, as #Fredou suggested:
static class ListExtension
{
public static T PopAt<T>(this List<T> list, int index)
{
T r = list[index];
list.RemoveAt(index);
return r;
}
}
and then call
String p1FirstCard = _deck.PopAt(0);
P.S. The name can be a bit confusing. Pop usually removes the last element, not the first one.
Building on AlexD's answer, I added a couple more extension methods:
public static class ListExtensionMethods
{
public static T PopAt<T>(this List<T> list, int index)
{
var r = list[index];
list.RemoveAt(index);
return r;
}
public static T PopFirst<T>(this List<T> list, Predicate<T> predicate)
{
var index = list.FindIndex(predicate);
var r = list[index];
list.RemoveAt(index);
return r;
}
public static T PopFirstOrDefault<T>(this List<T> list, Predicate<T> predicate) where T : class
{
var index = list.FindIndex(predicate);
if (index > -1)
{
var r = list[index];
list.RemoveAt(index);
return r;
}
return null;
}
}
If you want a direct equivalent to pop(), you'll have to write your own, because I don't think a List has a "Remove from end and return". However, there are both the Queue (first in, first out) and the Stack (first in, last out) classes instead of just a List.
There's also the LinkedList class which lets you add to or remove from both the beginning or the end, but the provided RemoveFirst() and RemoveLast() methods don't automatically return the item being removed - you'd need to write an extension method like AlexD's to do that.
All of these deal with removing things from the beginning or the end of the list. If you just want to remove an arbitrary item from the middle of a List, there's always List.Remove(item) which removes a specific item from the list (rather than by position).
private List<String> _deck = new List<String> {"2h", "3h", "4h", ... }
//Save into variable first
String p1FirstCard = _deck[0];
//Now just remove it
_deck.RemoveAt(0);
RemoveAt(int) doesn't return anything.

Remove item from List and get the item simultaneously

In C# I am trying to get an item from a list at a random index. When it has been retrieved I want it to be removed so that it can't be selected anymore. It seems as if I need a lot of operations to do this, isn't there a function where I can simply extract an item from the list? the RemoveAt(index) function is void. I would like one with a return value.
What I am doing:
List<int> numLst = new List<int>();
numLst.Add(1);
numLst.Add(2);
do
{
int index = rand.Next(numLst.Count);
int extracted = numLst[index];
// do something with extracted value...
numLst.removeAt(index);
}
while(numLst.Count > 0);
What I would like to do:
List<int> numLst = new List<int>();
numLst.Add(1);
numLst.Add(2);
do
{
int extracted = numLst.removeAndGetItem(rand.Next(numLst.Count));
// do something with this value...
}
while(numLst.Count > 0);
Does such a "removeAndGetItem" function exist?
No, as it's a breach of pure function etiquette, where a method either has a side effect, or returns a useful value (i.e. not just indicating an error state) - never both.
If you want the function to appear atomic, you can acquire a lock on the list, which will stop other threads from accessing the list while you are modifying it, provided they also use lock:
public static class Extensions
{
public static T RemoveAndGet<T>(this IList<T> list, int index)
{
lock(list)
{
T value = list[index];
list.RemoveAt(index);
return value;
}
}
}
public static class ListExtensions
{
public static T RemoveAndGetItem<T>(this IList<T> list, int iIndexToRemove}
{
var item = list[iIndexToRemove];
list.RemoveAt(iIndexToRemove);
return item;
}
}
These are called extension methods, call as new List<T>().RemoveAndGetItem(0).
Things to consider in the extension method
Exception handling with the index that you pass, check that the index is withing 0 and the count of the list before doing this.

Regarding Sorting MultiDimensional Arrays in C#

I am trying to figure out a way to correctly sort a bunch of different arraylists.
I am publishing content articles and every value [0] in an arraylist will relate to every other value [0]. and so on. Each element makes up the collective parts of a complete content item.
Now, the last element, popularity, is the amount of clicks an item has received. How do I
do a sort of the content items based on popularity without mixing up the html for each article?
*EDIT I am limited by the .NET 2.0 Framework at Work*
Below is the code... thanks.
public class MultiDimDictList : Dictionary<string, ArrayList> { }
myDicList.Add("fly", a_fly);
myDicList.Add("img", a_img);
myDicList.Add("bar", a_bar);
myDicList.Add("meter", a_meter);
myDicList.Add("block", a_block);
myDicList.Add("popularity", a_pop);
If you use the following code you can convert your existing dictionary of arraylists into a collection of Dictionaries and thus allowing a simple sort using Linq OrderBy
// Get the shortest arraylist length (they should be equal this is just a paranoia check!)
var count=myDicList.Values.Min(x=>x.Count);
// Get the collection of Keys
var keys=myDicList.Keys;
// Perform the conversion
var result=Enumerable.Range(0,count).Select(i=>keys.Select(k=>new {Key=k,Value=myDicList[k][i]}).ToDictionary(x=>x.Key,x=>x.Value));
var sorted=result.OrderByDescending(x=>x["popularity"]).ToList()
-- EDIT VERSION FOR .NET 2.0
First you need a comparer class
class PopularityComparison : IComparer<Dictionary<string,object>> {
private bool _sortAscending;
public PopularityComparison(bool sortAscending) {
_sortAscending = sortAscending;
}
public int Compare(Dictionary<string, object> x, Dictionary<string, object> y) {
object xValue = x["popularity"];
object yValue = y["popularity"];
// Sort Ascending
if (_sortAscending) {
return Comparer.Default.Compare(xValue, yValue);
} else {
return Comparer.Default.Compare(yValue, xValue);
}
}
}
Then you can use the following code
// Get the shortest arraylist length (they should be equal this is just a paranoia check!)
// Replacement for min
int count = int.MaxValue;
foreach (ArrayList a in myDicList.Values) if (a.Count < count) count = a.Count;
// Get the collection of Keys
Dictionary<string, ArrayList>.KeyCollection keys = myDicList.Keys;
// Perform the conversion
List<Dictionary<string, object>> result = new List<Dictionary<string, object>>(count);
for (int i = 0; i < count; i++) {
Dictionary<string, object> row = new Dictionary<string, object>(keys.Count);
foreach (string key in keys) row.Add(key, myDicList[key][i]);
result.Add(row);
}
And then finally to sort in ascending popularity order
result.Sort(new PopularityComparison(true));
or Descending order
result.Sort(new PopularityComparison(true));
I'd think it would be better to have an object containing your keys as properties, then a single collection with each item you'd have in your array lists.
This way you'd have a single collection sort, which becomes trivial if using Linq.OrderBy().
something like...
public class Article
{
public string Fly{get;set;}
public string Img{get;set;}
// etc.
public float Popularity{get;set;}
}
Then...
List<Article> articles = ... get from somewhere, or convert from your array lists.
List<Article> sorted = articles.OrderBy(a=>a.Popularity).ToList();
Please excuse the napkin code here... I'll update it if you need more detail.
An example using non-linq.
Create an implementation of IComparer.
public class ArticleComparer : IComparer<Article>
{
public bool Accending { get; set; }
public int Compare(Article x, Article y)
{
float result = x.Popularity - y.Popularity;
if (!Accending) { result *= -1; }
if (result == 0) { return 0; }
if (result > 0) return 1;
return -1;
}
}
Then when you go to sort the List, you can do something like the following.
ArticleComparer comparer = new ArticleComparer();
comparer.Accending = false;
articles.Sort(comparer);
This would be much easier if you had a list of article objects, each of which contained properties for fly, img, bar, popularity, etc. But if you really have to store things using this inside-out approach, then the only way you can sort the content items based on popularity is to create another array (or list) to hold the order.
Create a new list and populate it with sequential indexes:
List<int> OrderedByPopularity = new List<int>();
ArrayList popList = myDicList["popularity"];
for (int i = 0; i < popList.Count; ++i)
{
OrderedByPopularity.Add(i);
}
Now you have a list that contains the indexes of the items in the popularity list. Now you can sort:
OrderedByPopularity.Sort((i1, i2) => return popList[i1].CompareTo(popList[i2]););
But that gives you the least popular article first. If you want to reverse the sort so that OrderedByPopularity[0] is the most popular item:
OrderedByPopularity.Sort((i1, i2) => { return popList[i2].CompareTo(popList[i1]);});
Really, though, you should look into restructuring your application. It's much easier to work with objects that have properties rather than trying to maintain parallel arrays of properties.
If you have to do this in .NET 2.0, declare the poplist array at class scope (rather than method scope), and create a comparison method.
ArrayList poplist;
void MyMethod()
{
List<int> OrderedByPopularity = new List<int>();
popList = myDicList["popularity"];
for (int i = 0; i < popList.Count; ++i)
{
OrderedByPopularity.Add(i);
}
OrderedByPopularity.Sort(PopularityComparison);
// ...
}
int PopularityComparison(int i1, int i2)
{
return ((int)popList[i2]).CompareTo((int)popList[i1]);
}

Categories

Resources