Limit RemoveAll to a certain number of objects - c#

I am working with a List<T> which contains both parent and children objects. In this list children objects are aware of their related parent object and vice versa. Using this list I am trying to implement a business rule where up to 4 children objects will be removed from the list when their parent is of a certain type. Put differently if a parent of this type has 20 children 4 of them should be removed from the list.
The code I have outlined here will RemoveAll of the children objects that meet the condition. This is expected but what I'd like to do is limit the RemoveAll to removing only 4 children. Is there a means to do this with RemoveAll or is there another method I should be using?
myList.RemoveaAll(item =>
item.Child && "Foo".Equals(item.Parent.SpecialType));

The Take extension method is used to grab the first n number of matches from an IEnumerable. You can then iterate through the matches and remove them from the list.
var matches = myList.Where(item => item.Child && "Foo".Equals(item.Parent.SpecialType)).Take(someNumber).ToList();
matches.ForEach(m => myList.Remove(m));

Does it matter which 4? If not, you can use .Take(4) to create a list of 4 children, then iterate through and Remove the 4...

try this:
int i = 0;
myList.Removeall(item =>
item.Child && "Foo".Equals(item.Parent.SpecialType) && i++ < 4);
Note that I haven't tested it but it should work

Why not use the Take function?

You could also write an extension method to build on top of the normal list interface like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace App
{
public static class ListExtension
{
public static int RemoveAll<T>(this List<T> list, Predicate<T> match, uint maxcount)
{
uint removed = 0;
Predicate<T> wrappingmatcher = (item) =>
{
if (match(item) && removed < maxcount)
{
removed++;
return true;
}
else
{
return false;
}
};
return list.RemoveAll(wrappingmatcher);
}
}
public interface IHero { }
public class Batman : IHero { }
public class HeroCompilation
{
public List<IHero> Herolist;
public HeroCompilation()
{
Herolist = new List<IHero>();
}
public void AddBatmans(int count){
for (int i = 1; i <= count; i++) Herolist.Add(new Batman());
}
}
class Program
{
static void ConsoleWriteBatmanCount(List<IHero> hero)
{
Console.WriteLine("There are {0} Batmans", hero.Count);
}
static void Main(string[] args)
{
HeroCompilation tester = new HeroCompilation();
ConsoleWriteBatmanCount(tester.Herolist);
tester.AddBatmans(10);
ConsoleWriteBatmanCount(tester.Herolist);
tester.Herolist.RemoveAll((x) => { return true; }, 4);
ConsoleWriteBatmanCount(tester.Herolist);
tester.Herolist.RemoveAll((x) => { return true; }, 4);
ConsoleWriteBatmanCount(tester.Herolist);
tester.Herolist.RemoveAll((x) => { return true; }, 4);
ConsoleWriteBatmanCount(tester.Herolist);
while (true) ;
}
}
}

Related

Mongo C# driver sort with custom method

The situation: I am using MongoDb C# driver. I have an array string[] values. This is the code that I want to make work somehow:
var sort = Builders<Something>.Sort.Descending(x => values.Contains(x.Id));
I have implemented paging for my queries and for some reason I need a SortDefinition which sorts elements with id from a specific collection first and only after that returns other items.
Sadly I realized that Mongo driver Sort which is built in only allows sorting by field definition.
Try IComparable :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication192
{
class Program
{
static void Main(string[] args)
{
string[] inputs = { "abcdefg", "stuvwxy", "xyz123", "defghij" };
string[] outputs = inputs.Select(x => new CustomSort(x)).OrderBy(x => x).Select(x => x.id).ToArray();
}
}
public class CustomSort : IComparable<CustomSort>
{
static string[] order = { "def", "abc", "xyz" };
int index = 0;
public string id = "";
public CustomSort(string id)
{
this.id = id;
index = order.Select((x, i) => id.StartsWith(id) ? i : -1).Max(x => x);
}
public int CompareTo(CustomSort other)
{
int results = 0;
if (index == -1)
{
if (other.index == -1)
{
results = id.CompareTo(other.id); //neither in list order by string sort
}
else
{
results = 1; //this not i list, other is in list, other < this
}
}
else
{
if (other.index == -1)
{
results = -1; //this in list, othe not in list, this < other
}
else
{
index.CompareTo(other.index); //both in list use index
}
}
return results;
}
}
}

Checking list for custom object

If you have made a list of Custom objects is it a must to have to do with Hashcodes if you wanna check that list to see if it contains a object before adding it, I mean so that you do not get duplicates in the list or is there an easier way basically I want to use the contains method on a custom object list to see if the object I want to add already exists in the list and if there then is an easier way then to have to deal with hashcodes?
This is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DataConverter.Objects;
namespace DataConverter.Converters
{
class CategoryConverter
{
private Category category;
private SubCategory subCategory;
private ExcellObj excellObj;
public CategoryConverter(string path)
{
excellObj = new ExcellObj(path);
}
public List<Category> getCategoryListExcel()
{
List<Category> categories = new List<Category>();
List<string> ColumnNames = new List<string> { "Group1", "Group1Descr" };
List<int> CorrectColumn = new List<int>();
for(int i = 0; i < ColumnNames.Count; i++)
{
CorrectColumn.Add(excellObj.findColumn(ColumnNames[i]));
}
for(int i = 2; i < excellObj.allRows; i++)
{
categories.Add(category = new Category(excellObj.getValuesFromCell(i, CorrectColumn[1]), excellObj.getValuesFromCell(i, CorrectColumn[0]), "Home"));
}
return categories;
}
public List<List<SubCategory>> getSubCategory()
{
List<SubCategory> subCategories1 = new List<SubCategory>();
List<SubCategory> subCategories2 = new List<SubCategory>();
List<List<SubCategory>> subCategoriesList = new List<List<SubCategory>>();
List<string> ColumnNamesSubCategory1 = new List<string> { "Group2", "Group2Descr" };
List<string> ColumnNamesSubCategory2 = new List<string> { "Group3", "Group3Desc" };
List<int> CorrectColumn1 = new List<int>();
List<int> CorrectColumn2 = new List<int>();
for(int i = 0; i < ColumnNamesSubCategory1.Count; i++)
{
CorrectColumn1.Add(excellObj.findColumn(ColumnNamesSubCategory1[i]));
CorrectColumn2.Add(excellObj.findColumn(ColumnNamesSubCategory2[i]));
}
for(int i = 1; i < excellObj.allRows; i++)
{
subCategories1.Add(subCategory = new SubCategory(excellObj.getValuesFromCell(i, CorrectColumn1[1]),excellObj.getValuesFromCell(i,CorrectColumn1[0]), "Home"));
subCategories2.Add(subCategory = new SubCategory(excellObj.getValuesFromCell(i,CorrectColumn2[1]), excellObj.getValuesFromCell(i,CorrectColumn2[0]), "Home"));
}
subCategoriesList.Add(subCategories1);
subCategoriesList.Add(subCategories2);
return subCategoriesList;
}
public void finnishedUsingExcel()
{
excellObj.CloseApplication();
}
}
}
and what i whant to happen is that i whant to run a
if(categories.Contains(category) == false){
categories.add(category)
}
i do not understand this part in the documentation?
public Person(string lastName, string ssn)
{
if (Regex.IsMatch(ssn, #"\d{9}"))
uniqueSsn = $"{ssn.Substring(0, 3)}-{ssn.Substring(3, 2)}-{ssn.Substring(5, 4)}";
else if (Regex.IsMatch(ssn, #"\d{3}-\d{2}-\d{4}"))
uniqueSsn = ssn;
else
throw new FormatException("The social security number has an invalid format.");
this.LastName = lastName;
}
Assuming you have a code like this:
List<CustomObject> listOfCustomObjects = new List<CustomObject>();
Solution 1
If so, you can use listOfCustomObjects.Contains(customObject) to find out if customObject is in listOfCustomObjects. You should add using System.Linq; to the top of your code in order to use this method.
Solution 2
Another way to not have duplicates in your list is basically not using a List. You can use HashSet instead. With this method, duplicate objects won't be added to your list automatically. HashSet is also in LINQ Library, so you should add the line using System.Linq; for this solution too. Here's an example how to create a new HashSet with your CustomObject class:
HashSet<CustomObject> setOfCustomObjects = new HashSet<CustomObject>();
You really should have your class implement IEquatable if it's reasonable to do so and you're going to check for equality with any frequency, just so it does not bite you. The "Contains" method will work, but only to test that the exact same instance is present, not necessarily one that just shares matching properties. Consider the following code:
class Program
{
static void Main(string[] args)
{
var classInstance = new MySampleClass("testA", "testB");
var classList = new List<MySampleClass>();
classList.Add(classInstance);
if (classList.Contains(new MySampleClass("testA", "testB")))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
if (classList.Contains(classInstance))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
}
}
public class MySampleClass
{
public string SampleProperty1 { get; set; }
public string SampleProperty2 { get; set; }
public MySampleClass(string sampleProperty1, string sampleProperty2)
{
SampleProperty1 = sampleProperty1;
SampleProperty2 = sampleProperty2;
}
}
Even though we're checking for the presence of the class that has the exact same values as the one we previously added, they're still separate instances and you'll end up with duplicates in your list.
An alternative in the very limited case would be to use a LINQ method to check whether the list already contains an entry with a property that can be compared, such as an int ID or something:
yourList.Any(item => item.Id.Equals(otherItem.Id));
Again, if it's more than a one off, implement it the right way with IEquatable. See Microsoft's documentation

How to override ToString method for List of Lists

I'm pretty new with C# and I'm stuck on one university exercise. I have to sort some elements and them display them by overriden method ToString. And override is problem there. List works with ToString, List of Lists not.
Untill now I've tried to create some easy foreach and put it into overriden method, still, without success. It works only out of method. Asked my professor and he told me, it's because I'm acessing List, not Lista1, or something like that. Like I've said, I'm newbie.
using System;
using System.Collections.Generic;
namespace lista_7
{
class Lista
{
public static Random rand = new Random();
public List<int> nums = new List<int> ();
public Lista(int n)
{
for (int i = 0; i <= n; i++)
{
nums.Add(rand.Next(100));
}
}
public Lista()
{
Random rand = new Random();
for (int i = 0; i <= rand.Next(5); i++)
{
nums.Add(rand.Next(100));
}
}
public override string ToString()
{
String result = "";
foreach (var numbers in nums)
{
result += numbers + " ";
}
return result;
}
}
class Lista1 : Lista, IComparable<Lista1>
{
public Lista1(int n) : base(n) { }
public Lista1() : base() { }
public int CompareTo(Lista1 x)
{
if (x == this) return 0;
else if (x == null) return 1;
for (int i = 0; i < this.nums.Count && i < x.nums.Count; i++)
{
if (this.nums[i] > x.nums[i]) return 1;
else if (this.nums[i] < x.nums[i]) return -1;
}
return x.nums.Count > this.nums.Count ? -1 : 1;
}
}
class Program
{
static void Main(string[] args)
{
List<Lista1> fir = new List<Lista1> {
new Lista1(5),
new Lista1(),
new Lista1(3)
};
foreach (var variable in fir)
{
//Console.Write ( toString )
foreach (var x in variable.nums)
{
Console.Write(x + " ");
}
Console.WriteLine();
}
Console.WriteLine(fir.ToString());
Console.ReadLine();
}
}
}
I would describe errors, but I dont have any. fir.ToString shows System.Collections.Generic.List`1[lista_7.Lista1]
Put your fir.ToString() inside your loop as variable.ToString():
foreach (var variable in fir)
{
Console.WriteLine(variable.ToString());
}
I'm not sure it's a good idea to override ToString() method, I'd rather create an extension method for this, but that's about how it should work.
From the comments, he's requiring you to inherit from a list and override its ToString() method, and then inherit from a list of that list and override its ToString() method.
First, the inner list (for lack of a better term):
public class InnerList : List<int>
{
public override string ToString()
{
// return the contents of the list somehow formatted as strings.
return string.Join(" ", this);
}
}
Next, a list of that list:
public class OuterList : List<InnerList>
{
public override string ToString()
{
var result = new StringBuilder();
ForEach(inner =>
{
if (inner != null) result.AppendLine(inner.ToString());
});
return result.ToString();
}
}
The ToString() method of the outer list works by calling the ToString() method of all of the lists it contains.
I'm obviously making up what goes in the overridden methods. But this is how you would override them according to the requirements you specified. In "real life" this wouldn't make any sense. We might declare a List<List<int>> without creating any new classes, and then write code to render it as a string. We could write code to render the same List<List<int>> two different ways. If we do this by overriding ToString() it suggests that we would create different inherited classes to format the numbers differently.
(Some courses tend to over-emphasize inheritance. It's useful, but they make it seem like it's the answer to everything.)
Note that there's also no need for either list to populate itself. That just makes it more confusing. You can write separate code to add numbers to it.

Accessing properties of elements in a collection to create new collections

Is there any way to define a table as a collection of rows, and automatically populate properties (columns) on the table according to the properties of the rows?
For example:
public class Foobar {
public int TheNumber;
public string TheString;
}
public class SomeFoobars : List<Foobar> {
public List<int> TheNumber {
get { return Select(foo => foo.TheNumber); }
set { for (int i = 0; i < Count; i++) { this[i].TheNumber = value[i]; }
}
public List<int> TheString {
get { return Select(foo => foo.TheString ); }
set { for (int i = 0; i < Count; i++) { this[i].TheString = value[i]; }
}
}
// So I can now do things like:
SomeFoobars myFoobars = ReturnsListOfFoobar();
MethodThatTakesListOfInt( myFoobars.TheNumbers );
myFoobars.TheString = SomeMethodThatReturnsListOfString();
Creating the collection class implementation isn't so bad if you only have to do it once, but I would like to have this functionality for any type of row and not have to write the collection properties over and over. These property methods are essentially identical, other than the reference to the specific property on the contained class (i.e. TheNumber or TheString in the example above).
Is there any way to accomplish this? Perhaps using reflection?
I would suggest you to go back and revise your design. As you may realize now, it is causing a lot of trouble to you.
With that being said if you still decide to keep the current kits, you can remove the properties on SomeFoobars and still do the same in this way :
MethodThatTakesListOfInt(
myFoobars
.Select(f => f.TheNumber)
.ToList());
SomeMethodThatReturnsListOfString()
.Select((s,i) => new { Index = i, String = s })
.ToList()
.ForEach(x => myFoobars[x.Index].TheString = x.String);

Why my ConcurentSet doesn't work?

I was asking for any ConcurentSet/ConcurentQueue implementation here how to create no-duplicates ConcurrentQueue? but noone suggested anything.
So I've decided to wrote it myself, modified MSDN example slightly.
I've created ConcurentSet class. I insert the same element twice, and expect it to be inserted only once, because Set is used inside:
numbers.Add(i);
Console.WriteLine("Add:{0} Count={1}", i, numbers.Count);
numbers.Add(i);
Console.WriteLine("Add:{0} Count={1}", i, numbers.Count);
However according to output elements inserted twice:
Add:0 Count=0
Add:0 Count=1
Add:1 Count=2
Add:1 Count=3
Add:2 Count=4
Take:0
Add:2 Count=5
Add:3 Count=6
Add:3 Count=7
Add:4 Count=7
Add:4 Count=8
Take:3
Add:5 Count=9
Add:5 Count=10
Add:6 Count=11
Add:6 Count=12
The question is - why my ConcurentSet implementation doesn't work as expected?
I expect such output:
Add:0 Count=0
Add:0 Count=0
Add:1 Count=1
Add:1 Count=1
Add:2 Count=2
Take:0
Add:2 Count=1
Add:3 Count=2
Add:3 Count=2
Add:4 Count=3
Add:4 Count=3
Take:3
Add:5 Count=3
Add:5 Count=3
.....
Full listing below:
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace BCBlockingAccess
{
using System;
using System.Collections.Concurrent;
public class ConcurentSet
: IProducerConsumerCollection<int>
{
private readonly object m_lockObject = new object();
private readonly HashSet<int> m_set = new HashSet<int>();
public void CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
public int Count { get { return m_set.Count; } }
public object SyncRoot { get { return m_lockObject; } }
public bool IsSynchronized { get { return true; } }
public void CopyTo(int[] array, int index)
{
throw new NotImplementedException();
}
public bool TryAdd(int item)
{
lock (m_lockObject)
{
m_set.Add(item);
}
return true;
}
public bool TryTake(out int item)
{
lock (m_lockObject)
{
foreach (var i in m_set)
{
if (m_set.Remove(i))
{
item = i;
return true;
}
}
item = -1;
return false;
}
}
public int[] ToArray()
{
throw new NotImplementedException();
}
public IEnumerator<int> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
class Program
{
static void Main(string[] args)
{
// Increase or decrease this value as desired.
int itemsToAdd = 50;
// Preserve all the display output for Adds and Takes
Console.SetBufferSize(80, (itemsToAdd * 5) + 3);
// A bounded collection. Increase, decrease, or remove the
// maximum capacity argument to see how it impacts behavior.
BlockingCollection<int> numbers = new BlockingCollection<int>(new ConcurentSet());
// A simple blocking consumer with no cancellation.
Task.Factory.StartNew(() =>
{
int i = -1;
while (!numbers.IsCompleted)
{
try
{
i = numbers.Take();
}
catch (InvalidOperationException)
{
Console.WriteLine("Adding was compeleted!");
break;
}
Console.WriteLine("Take:{0} ", i);
// Simulate a slow consumer. This will cause
// collection to fill up fast and thus Adds wil block.
Thread.SpinWait(100000);
}
Console.WriteLine("\r\nNo more items to take. Press the Enter key to exit.");
});
// A simple blocking producer with no cancellation.
Task.Factory.StartNew(() =>
{
for (int i = 0; i < itemsToAdd; i++)
{
numbers.Add(i);
Console.WriteLine("Add:{0} Count={1}", i, numbers.Count);
numbers.Add(i);
Console.WriteLine("Add:{0} Count={1}", i, numbers.Count);
}
// See documentation for this method.
numbers.CompleteAdding();
});
// Keep the console display open in debug mode.
Console.ReadLine();
}
}
}
BlockingCollection maintains its own count and doesn't use count of underlying ConcurrentSet of yours therefore even if duplicates are ignored, the count increases.
You might want to write your own wrapper around this collection which returns count from the concurrentset and relays every other method property to the blocking collection.

Categories