How to delete item from LinkedList by specific compare? - c#

I have LinkedList there is Remove(item) method that get as param item.
I would like to know what I have to override to delete item by specific param?
For example I have LinkedList<MyClass>, in MyClass I have variable int index and I would like to compare by this value...
Like, I am looking for some override compare(Obj) method that I can override and compare objects when I delete items from LinkedList by value that I need...
EDIT
Method Where does not fit, because actually I have a generic LinkedList implementation and actually my LinkedList looks like LinkedList<TYPE>, and it could be any of type. Because of this I can't use where because actually I don't know the exact type

The most simple way is to set a variable that will check if each value in a linked way is the one you're looking to delete and ALSO set a variable that is one value/step behind whatever the first variable is tracking.
That way, when the main variable (the one doing the checking) spots the list value you want to remove, you set the behind variable's "next value" equal to the main variable's "next value" thus overwriting and deleting it.
Each time you increment the main variable, also increment the behind variable so that you can keep it one step behind.
DIAGRAM (left is before, right is after): https://gyazo.com/4d989b6ff6249249c9d63a17a830a8c1
Basically, just set two variables: one that is checking each linked list value to find the one you're looking to delete and one BEHIND it so that when you find the value you want to delete, you set the variable that's behind it to the value in front of the main variable thus overwriting the deleted part.

using System;
using System.Collections.Generic;
namespace ConsoleAppCore
{
public static class Extension
{
public static List<dynamic> Where(this IEnumerable<dynamic> list, Func<dynamic, bool> func)
{
List<dynamic> result = new List<dynamic>();
foreach(dynamic item in list) {
try {
if (func(item))
result.Add(item);
}
catch {
continue;
}
}
return result;
}
}
class YourClass
{
public int x = 5;
}
class Program
{
static void Main(string[] args)
{
LinkedList<dynamic> list = new LinkedList<dynamic>();
list.AddAfter(list.AddAfter(list.AddAfter(list.AddAfter(
list.AddAfter(list.AddAfter(list.AddAfter(list.AddFirst(
(decimal)1), 2), (double)3), "Hello"), 5), new YourClass()), (float)7), 8);
var newlist = list.Where(i => i == "Hello");
// only one logical operation at a time (caused exceptions break the logic)
newlist.AddRange(list.Where(i => i.x == 5));
newlist.AddRange(list.Where(i => i > 5));
foreach(var i in newlist)
Console.WriteLine(i);
}
}
}
Output
Hello, ConsoleAppCore.YourClass, 7, 8

Related

Reassign object in foreach loop c#

Not sure I understand why I can do this with a for loop and not a foreach loop?
This is the code that works. Looping through a BindingList Products, finding a match and then assigning that product at index i to the new product that's passed in.
public static void UpdateProduct(int productToUpdateID, Product productToUpdate)
{
for (int i = 0; i < Products.Count; i++)
{
if (Products[i].ProductID == productToUpdateID)
{
Products[i] = productToUpdate;
}
}
}
If I try to do this with a foreach loop I get an error that I cannot assign to the iterator variable. What is the reasoning for this and is there a way to get around it or is using a for loop for this kind of problem the best solution?
This is essentially what I'm trying to do.
public static void UpdateProduct(int productToUpdateID, Product productToUpdate)
{
foreach(Product product in Products)
{
if (product.ProductID == productToUpdateID)
{
product = productToUpdate;
}
}
}
I can do something like this and reassign all the properties explicitly but want to see if there is another way to do it.
foreach(Product product in Products)
{
if (product.ProductID == productToUpdateID)
{
product.Name = productToUpdate.Name;
}
}
Thanks!
The foreach construct is for when you want to do something with each item in the list. That does not seem to be what you are doing. You are modifying the list itself, by removing an item and replacing it.
Personally I would not use a loop at all, I'd just remove the old item and add the new one.
public static void UpdateProduct(int productToUpdateID, Product productToUpdate)
{
Products.RemoveAll( x => x.ProductID == productToUpdateID );
Products.Add( productToUpdate );
}
Or if you wish to preserve order:
public static void UpdateProduct(int productToUpdateID, Product productToUpdate)
{
var index = Products.FindIndex( x => x.ProductID == productToUpdateID );
Products[index] = productToUpdate;
}
The reasons have already been given, but as a minor detail: this is sometimes possible; there is an alternative syntax in recent C# that uses a ref-local for the iterator value:
foreach (ref [readonly] SomeType value in source)
which is only available for some scenarios - naked arrays, spans, or custom iterator types with a ref-return Current - and as long as the optional readonly modifier is not used, you can assign directly via the value variable, since this is a direct reference to the underlying source. The uses for this are rare and niche. If Products is a List<T>, you could combine this with CollectionMarshal.AsSpan(...) to achieve what you want, but frankly I'd consider that hacky (apart from other things, it would bypass the list's internal change protections). Basically: don't do this, but : it isn't entirely impossible.
The foreach loop iterates over the elements of a collection, and the iteration variable is simply a reference to the current element in the collection.
The reason you cannot modify the iteration variable itself is that it is a read-only reference to the element in the collection. Modifying the iteration variable would not change the element in the collection; it would only change the reference.
Alternative ways are already mentioned in the above answers.
Just for the record. IMHO the best way is to use a foreach loop with a modified code like this. It only makes one iteration
int i=-1;
foreach (var product in products )
{
i++;
if (product.ProductID == productToUpdate.ProductID)
{
products[i]=productToUpdate;
break;
}
}
But if you want to use linq for some reason, you can do it in one line
products = products.Select(x => x = x.ProductID == productToUpdate.ProductID?productToUpdate:x).ToList();

C# How to iterate through nullable list? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I am interfacing to an API that exposes the following function:
IEnumerable<Color?> getColors(long Id);
I call the function:
IEnumerable<System.Drawing.Color?> colList = getColors(1);
The list is not null when the function returns. However, when I attempt to iterate through the list:
foreach (System.Drawing.Color col in colList)
{...}
I get nothing. The loop is not entered.
What is the correct way to iterate through the list?
Edit: I finally figured out how to get a list count with this bit of voodoo:
int colCount = colList.Count<System.Drawing.Color?>();
The count is indeed zero, as has been suggested. I'm now off to the provider of the API to ask why so?
Thank you to all who provided positive and constructive suggestions.
The iterator is empty. That is why it does not enter the loop.
However, please note that you do not have a nullable type in your loop:
IEnumerable<System.Drawing.Color?> colList = getColors(1);
// System.Drawing.Color is not nullable:
foreach (System.Drawing.Color col in colList)
{
Console.WriteLine(col);
}
Which will result in an InvalidOperationException when a null comes (because it cannot cast null to System.Drawing.Color). Use a System.Drawing.Color? (or use var) instead.
Example:
static void Main()
{
foreach (var col in getColors(1))
{
Console.WriteLine(col == null);
}
Console.ReadLine();
}
static IEnumerable<System.Drawing.Color?> getColors(long Id)
{
yield return null;
}
Outputs True
About using the extension method Count, know that it might be iterating over the IEnumerable<Color?>※. In that case, I would suggest to use ToArray and check the size of the array. With that said, know that a IEnumerable<Color?> could also be infinite.
※: As per the reference source, Count could be casting to ICollection<TSource> or ICollection to get the Count property from there. Otherwise it will iterate.
Trivial example of infinite IEnumerable<Color?>:
static IEnumerable<System.Drawing.Color?> getColors(long Id)
{
while (true)
{
yield return null;
}
}
If you need to handle the empty case, another option is to set a variable inside the loop. If the variable is set, you know it is not empty.
Example:
bool isEmpty = true;
foreach (var col in getColors(1))
{
isEmpty = false;
// ...
}
if (isEmpty)
{
// ...
}
As Dmitry Bychenko points out, you might be interested in OfType.
Addendum:
The IEnumerable<T> interface has only a GetEnumerator method. However, C# supports extension methods, and you will find a large number of extension methods for IEnumerable<T> in System.Linq.Enumerable, including Count, ToArray and OfType.
To use these extension methods add using System.Linq; to your code file. Example:
using System.Linq;
// ...
static void Main()
{
var colors = getColors(1).ToArray();
// ...
}
Or call them as regular methods. Example:
static void Main()
{
var colors = System.Linq.Enumerable.ToArray(getColors(1));
// ...
}
These last two code examples are equivalent. Usually the former is prefered.
As you can see, Color? is nullable, when Color is not. If you want to get only not null values (Color), you can add Linq OfType<T>():
using System.Linq; // OfType<T>() is declared as Linq extension method
...
// loop over not null cols only
foreach (System.Drawing.Color col in colList.OfType<System.Drawing.Color>()) {
...
}
Demo:
using System.Linq;
...
List<System.Drawing.Color?> list = new List<System.Drawing.Color?>() {
System.Drawing.Color.Red,
System.Drawing.Color.Black,
null, // <- should be excluded
System.Drawing.Color.Blue,
};
foreach (System.Drawing.Color col in list.OfType<System.Drawing.Color>())
Console.WriteLine(col);
Outcome:
Color [Red]
Color [Black]
Color [Blue]
This is not in relation to the nullable values of the IEnumerable. If you run this code (https://rextester.com/):
List<String> list = new List<String>();
list.Add(null);
list.Add("asd1");
list.Add(null);
list.Add("asd2");
list.Add(null);
IEnumerable<String> enumerable = list.AsEnumerable();
foreach(var item in enumerable)
{
Console.WriteLine('"' + item + '"');
}
The output will be:
""
"asd1"
""
"asd2"
""
So as you can see the null values are not removed from the IEnumerable and we still iterate through the empty values. More than likely the IEnumerable is empty.

How to find specific object from list in C#?

How is it possible to find a specific object from a list?
Lets say i have a function that takes an object and a list that contains objects of this type and returns the number at which position the specific object is found.
The only way i could think of a solution is to run the list through with a foreach loop, but isn't there a better way?
Thanks
You can use the IndexOf(T item) method:
myList.IndexOf(myItem);
It returns the index of the first occurrence of the item.
The only way i could think of a solution is to run the list through with a foreach loop
Generally, you need a loop (a for or foreach) to find an object in a list. You could use it directly, or through a function that iterates over list elements, but there is going to be a loop. There is no way around it: unless you know something special about the way the elements of the array are arranged, you have to look at them all.
One case of knowing something special about arrangement of elements is knowing that an array is sorted. If this is the case, and when you know the value of the attribute on which the element is sorted, you can find the element much faster by using binary search.
You could use linq expressions
List.where(condition)
Or
List.Select(condition).FirstOrDefault
Based on search condition it will return the item you want.
You can use method IndexOf or if you use a special condition then you can use method
public int FindIndex(Predicate<T> match);
where match is delegate
public delegate bool Predicate<T>(T obj);
In fact it is similar to standard C++ algorithm std::find_if
To see whether object is there You might just need List<T>.Contains method
It states,
Determines whether an element is in the List.
And you need to use it like List<T>.Contains(T type item) , where T is the same type of List and item you need to compare. In your case it's a the type of Object
And to return the index you can use List<T>.IndexOf Method
Searches for the specified object and returns the zero-based index of the first occurrence within the entire List.
Simple Console program
class Program
{
static void Main(string[] args)
{
MyType a = new MyType() { id = 10 };
MyType b = new MyType() { id = 20 };
MyType c = new MyType() { id = 30 };
List<MyType> testList = new List<MyType>();
testList.Add(a);
testList.Add(b);
Console.WriteLine(testList.Contains(a)); // <= Will return true
Console.WriteLine(testList.Contains(c)); // <= Will return false
Console.WriteLine(testList.IndexOf(a)); // <= will return 0 : the index of object a
Console.ReadKey();
}
}
// A simple class
class MyType
{
private int ID;
public int id
{
get { return ID; }
set { ID = value; }
}
}

How to make extension unionWith for Hashset

I am trying to make an extension for the custom type. This is my code. I don't know how my source becomes zero in this code. Even in the debug part hashset temp is giving me a list of 10 logevents. But in the final the source is becoming zero.
public static void UnionSpecialWith(this HashSet<LogEvent> source, List<LogEvent> given,IEqualityComparer<LogEvent> comparer)
{
List<LogEvent> original = new List<LogEvent>(source);
List<LogEvent> second = given.Condense(comparer);
source = new HashSet<LogEvent>(original.Condense(comparer),comparer);
foreach (LogEvent logEvent in second)
{
if (original.Contains(logEvent, comparer))
{
int index = original.FindIndex(x => comparer.Equals(x, logEvent));
original[index].filesAndLineNos.MergeFilesAndLineNos(logEvent.filesAndLineNos);
}
else
original.Add(logEvent);
}
#if DEBUG
String content = String.Join(Environment.NewLine, original.Select(x => x.GetContentAsEventsOnly()));
HashSet<LogEvent> temp = new HashSet<LogEvent>(original, comparer);
#endif
source = new HashSet<LogEvent>(original, comparer);
}
Can anybody point me out what is wrong?
EDIT:
This is my custom type. Whenever I found a duplicate , I want to merge it's "filesAndLineNos" with the original one. This is what I am trying to achieve with the above code.
public class LogEvent
{
public String mainEventOriginal;
public String subEventOriginal;
public String mainEvent;
public String subEvent;
public int level;
public Dictionary<String,HashSet<int>> filesAndLineNos;
}
The usage is something like
HashSet<LogEvent> required = new HashSet<LogEvent>(initialUniqueSet);
required.UnionSpecialWith(givenListOfLogEvents);
This is simply a matter of parameters being passed by value in .NET by default. You're changing the value of source to refer to a different HashSet, and that doesn't change the caller's variable at all. Assuming that Condense doesn't modify the list (I'm unaware of that method) your method is as pointless as:
public void TrimString(string text)
{
// This changes the value of the *parameter*, but doesn't affect the original
// *object* (strings are immutable). The caller won't see any effect!
text = text.Trim();
}
If you call the above with:
string foo = " hello ";
TrimString(foo);
... then foo is still going to refer to a string with contents " hello ". Obviously your method is more complicated, but the cause of the problem is the same.
Either your extension method needs to modify the contents of the original HashSet passed in via the source parameter, or it should return the new set. Returning the new set is more idiomatically LINQ-like, but HashSet.UnionWith does modify the original set - it depends which model you want to be closer to.
EDIT: If you want to modify the set in place, but effectively need to replace the contents entirely due to the logic, then you might want to consider creating the new set, then clearing the old and adding all the contents back in:
public static void UnionSpecialWith(this HashSet<LogEvent> source,
List<LogEvent> given,
IEqualityComparer<LogEvent> comparer)
{
List<LogEvent> original = new List<LogEvent>(source);
List<LogEvent> second = given.Condense(comparer);
foreach (LogEvent logEvent in second)
{
if (original.Contains(logEvent, comparer))
{
int index = original.FindIndex(x => comparer.Equals(x, logEvent));
original[index].filesAndLineNos
.MergeFilesAndLineNos(logEvent.filesAndLineNos);
}
else
{
original.Add(logEvent);
}
}
source.Clear();
foreach (var item in original)
{
source.Add(item);
}
}
However, note:
This does not replace the comparer in the existing set. You can't do that.
It's pretty inefficient in general. It feels like a Dictionary would be a better fit, to be honest.

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.

Categories

Resources