C# remove null values from object array - c#

I got an array of a specific object. Lets say the object Car. At some point in my code I need to remove all Car-objects from this array that do not fulfill the requirements I stated. This leaves null values in the array.
public class Car{
public string type { get; set; }
public Car(string ntype){
this.type = ntype;
}
}
Car[] cars = new Car[]{ new Car("Mercedes"), new Car("BMW"), new Car("Opel");
//This should function remove all cars from the array where type is BMW.
cars = removeAllBMWs(cars);
//Now Cars has become this.
Cars[0] -> Car.type = Mercedes
Cars[1] -> null
Cars[2] -> Car.type = Opel
//I want it to become this.
Cars[0] -> Car.type = Mercedes
Cars[1] -> Car.type = Opel
Of course my real code is far more complex than this, but the base idea is the same. My question that I have is: How can I remove the empty values from this array?
I found countless solutions for a string array, but none for an object array.

The following will create a new array with all the null values excluded (which seems to be what you actually want?):
Cars = Cars.Where(c => c != null).ToArray();
Better yet, define your RemoveAllBMWs method to omit the BMWs in the first place instead of setting them to null:
internal static Car[] RemoveAllBMWs(IEnumerable<Car> cars)
{
return cars.Where(c => c != null && c.Type != "BMW").ToArray();
}

Related

When calling .ToList() on an IEnumerable which selects part of an existing list, why is the original list updated when the new list is modified?

I have a list of apples, and I find those which are red:
var redApples = apples.Where(a => a.colour == "red");
redApples is an IEnumerable here, so if I convert this to a list:
var redApplesList = redApples.ToList();
This gives me a list of new objects. So if I modify these objects:
redApplesList.ForEach(a => a.color = "blue");
I would not expect the items in my original apples list to change colour at all. But this isn't what happens, the apples in my original list which were "red" are now "blue".
What is the misunderstanding here?
I was under the impression ToList() created a completely new list of items independent from existing lists, but this suggests the pointers in the original list were updated to point to the newly created objects? Is this what's happening under the hood?
A lot of developers seem to resort to separating things out into separate lists (or even cloning objects) because they're never 100% sure what they're working with. It would be helpful to have a confident understanding of this area.
It gives you a new list, but that list contains references (apple is a class = reference type) to the same objects like the original list. They are not copied so when you reference one of them via the second list and change it, it is the same original item that is updated.
If you are looking to copy the items look into deep copy and one way is by using ICloneable (another way as Kyle commented is using a copy constructor)
If you implement you class like this:
public class Apple : ICloneable
{
public string Color { get; set; }
public object Clone()
{
return new Apple { Color = Color };
}
}
Then check this:
List<Apple> apples = new List<Apple>
{
new Apple { Color = "red" },
new Apple { Color = "blue" },
};
var redAppels = apples.Where(a => a.Color == "red").Select(a => (Apple)a.Clone()).ToList();
redAppels[0].Color = "green";
Console.WriteLine($"Original: {apples[0].Color}, new: {redAppels[0].Color}");
// Original red, new: green
Without the call to .Clone as in your you get the same references. With the .Clone you get new objects. Thus, when you change their Color it does not effect the original
After reading a bit more (Copy constructor versus Clone()) I'd suggest go for copy constructor instead:
public class Apple
{
public Apple() { }
public Apple(Apple apple)
{
Color = apple?.Color;
}
public string Color { get; set; }
}
var redAppels = apples.Where(a => a.Color == "red")
.Select(a => new Apple(a))
.ToList();
It doesn't create a list of new objects. It creates a new list of the existing objects.

Setting a Property via Reflection in a copied List updates original list

I'm experiencing a problem in my C# code and I'm sure it has to do with the way I'm using reflection, but I'm not sure how to fix it.
To the best of my knowledge, if I have:
List1 = List<MyClass>
and use a syntax similar to
List2 = new List<MyClass>(List1);
Then List2 should be a copy of List1 and any updates made to it should not reflect in the original list.
That being the case, consider the following test code:
public class Fake
{
public string MyVal { get; set; }
}
public List<Fake> List1;
public List<Fake> List2;
public void UpdateClassTest()
{
List1 = new List<Fake>() {new Fake() { MyVal = "hello" } };
List2 = new List<Fake>(List1);
string propName;
System.Type type = typeof(Fake);
foreach (System.Reflection.PropertyInfo pi in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
{
propName = pi.Name;
List2.ForEach(f => pi.SetValue(f, "Good Bye"));
}
}
When I run this Both List1[0] and List2[0] get updated to "Good Bye", but I would assume List1 would not be affected by the changes I'm making to List2.
What am I doing wrong or not understanding here?
new List(List other) does not do a deep copy. When you modify the item at [0] it's modifying the original object, which exists in both lists.
See this other question about implementing ICloneable.
Copying the list means that the lists are different objects. The elements contained by the lists are still the same. For instance:
List1 = new List<Fake>() {new Fake { MyVal = "hello" } };
List2 = new List<Fake>(List1);
List2.Add(new Fake { MyVal = "hey" });
Console.WriteLine(List1.Length); // 1
Console.WriteLine(List2.Length); // 2
List2[0].MyVal = "hi";
Console.WriteLine(List1[0].MyVal) // hi

Sort List without creating new variable

I'm attempting to use Enumerable.OrderBy to sort a List because ultimately I want to be able to sort by more than a single field. At the moment it only appears to work if I create a new variable var to hold the results view which means (I think) the types need to be re-cast.
Is there a method to sort a List by more than 1 field whilst retaining the original List variable and types? I.e. I'd rather end up with variable _orderedbins of type List<orderedbins>
Below is what I currently have but everything from var test = ... onwards seems a bit wrong.
public class orderedBins
{
public string Bin { get; set; }
public int Order { get; set; }
}
List<orderedbins> _orderedbins = new List<orderedbins>();
foreach (string item in splitbins)
{
string[] spbinSetting = item.Split(',');
bool bchecked = bool.Parse(spbinSetting[1]);
int border = int.Parse(spbinSetting[2]);
if (bchecked == true)
{
_orderedbins.Add(new orderedbins { bin = spbinSetting[0], Order = border });
}
}
var test =_orderedbins.OrderBy(x => x.Order);
foreach (var item in test)
{
string f = item.Bin;
int g = item.Order;
}
You know, you can perform multiple sub-sorts for an order by...
lst.OrderBy(x => x.Prop1).ThenBy(x => x.Prop2).ThenByDescending(x => x.Prop3)...
Just add a .ToList(); and introduce it with a variable, to have the result in a list variable.
EDIT:
Great suggestion by Willem, for more readability:
from x in lst
order by x.Prop1, x.Prop2, x.Prop3
select x
You can create a new sorted list without creating a new variable using
list = list.OrderBy(item => item.Field1).ThenBy(item => item.Field1).ToList();
It will still create an entirely new list though (it's not actually much of a problem to add a new variable; those are cheap. Creating a new list, doing this, is fine as long as the list isn't very large.
If you need to sort the list in place then you'll want to use a custom comparer with the List's sort method:
public class MyComparer : IComparer<MyClass>
{
public int Compare(MyClass x, MyClass y)
{
if(x.Field1 != y.Field1)
return x.Field1.CompareTo(y.Field1)
else
return x.Field2.CompareTo(y.Field2);
}
}
List<MyClass> list = new List<MyClass>();
//Populate list
list.Sort(new MyComparer());
As others suggested, using Linq's OrderBy(...).ToList() would be a cleaner way, but this will give you a new instance of the list.
To retain the original instance, consider to use List<T>.Sort():
_orderedbins.Sort(new Comparison<orderedBins>((obj1, obj2) =>
{
int result = obj1.Order.CompareTo(obj2.Order);
return result != 0 ? result : obj1.Bin.CompareTo(obj2.Bin);
}));
This will do the trick:
_orderedbins = _orderedbins.OrderBy(x => x.Order).ToList();
...but there's no real issue creating a new variable/reference.
I think this will do it (it's already a list of orderbins so no casting is required):
_orderbins = _orderbins.OrderBy(x => x.Order).ToList();

How to maintain unique List Before Saving To Database? - C#

A simplified scenario:
I have a List<Foo>.
Foo has two properties Description (string), IsFoo (bool)
E.g:
var foos = new List<Foo>();
User can "add new Foo's" via textboxes, then on form submit i do this:
foos.Add(new Foo { Description = txtOne.Text, IsFoo = true });
foos.SaveToDb();
However, there are multiple textboxes, and if for example they type "FooBar" in textbox one, then "FooBar" in textbox two, i do not want to show an error, but i simply do not want to add them to the collection. (don't worry about the reason behind this, this is a simplified scenario).
I don't need to show anything to the UI, just when they submit the form, before persisting to the database i need to remove any duplicates (or prevent them from being added to the list in the first place).
What's the easiest/best way to do this? Dictionary perhaps?
I'm using C#4, LINQ, .NET 4.
You can use a HashSet<Foo>.
HashSets are unique, unordered collections.
Adding an element that already exists will silently do nothing. (and return false)
Note that you must override Equals and GetHashCode on the Foo class to compare by value.
Also note that hashsets are intrinsically unordered; if you care about insertion order, you can't use it.
Alternatively, you can use LINQ to check whether your list has a duplicate:
if (!foos.Any(f => f.Description == txtOne.Text))
foos.Add(new Foo { Description = txtOne.Text, IsFoo = true });
To expand on SLaks' answer, you could do something like this:
public class FooComparer : IEqualityComparer<Foo> {
public static readonly FooComparer Instance = new FooComparer();
private FooComparer() { }
public bool Equals(Foo a, Foo b) {
if (a == null)
return b == null;
if (b == null)
return false;
// For case-sensitivity:
return a.Description == b.Description;
// For case-insensitivity:
return String.Equals(a.Description, b.Description, StringComparison.OrdinalIgnoreCase);
}
public int GetHashCode(Foo obj) {
// For case-sensitivity:
return obj.Description.GetHashCode();
// For case-insensitivity:
return StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Description);
}
}
Then store your items in a HashSet<Foo> like so:
var hashSet = new HashSet<Foo>(FooComparer.Instance);
hashSet.Add(new Foo() { ... });
With this code, if a second Foo object is added to the hashset and has an identical description as one already present in the hashset, the new object will simply not be added.
Can you use Distinct in linq?
This is VB (and not accurate as I've not got VS on this machine), but something along the lines of:
Dim ie as IEnumerable(of Foo) = From obj As Foo In Foo's Select obj Distinct
Then implent IEqualityComparer? - lookd like #cdhowie just answered....

how to check if object already exists in a list

I have a list
List<MyObject> myList
and I am adding items to a list and I want to check if that object is already in the list.
so before I do this:
myList.Add(nextObject);
I want to see if nextObject is already in the list.
The object "MyObject" has a number of properties but comparison is based on matching on two properties.
What is the best way to do a check before I add a new "MyObject" to this list of "MyObject"s.
The only solution I thought up was to change from a list to a dictionary and then make the key a concatenated string of the properties (this seems a little unelegant).
Any other cleaner solutions using list or LINQ or something else?
It depends on the needs of the specific situation. For example, the dictionary approach would be quite good assuming:
The list is relatively stable (not a lot of inserts/deletions, which dictionaries are not optimized for)
The list is quite large (otherwise the overhead of the dictionary is pointless).
If the above are not true for your situation, just use the method Any():
Item wonderIfItsPresent = ...
bool containsItem = myList.Any(item => item.UniqueProperty == wonderIfItsPresent.UniqueProperty);
This will enumerate through the list until it finds a match, or until it reaches the end.
Simply use Contains method:
bool alreadyExist = list.Contains(item);
Note that it works based on the equality function Equals. Check the example of the link above if you need to implement Equals function.
If it's maintainable to use those 2 properties, you could:
bool alreadyExists = myList.Any(x=> x.Foo=="ooo" && x.Bar == "bat");
Are you sure you need a list in this case? If you are populating the list with many items, performance will suffer with myList.Contains or myList.Any; the run-time will be quadratic. You might want to consider using a better data structure. For example,
public class MyClass
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class MyClassComparer : EqualityComparer<MyClass>
{
public override bool Equals(MyClass x, MyClass y)
{
if(x == null || y == null)
return x == y;
return x.Property1 == y.Property1 && x.Property2 == y.Property2;
}
public override int GetHashCode(MyClass obj)
{
return obj == null ? 0 : (obj.Property1.GetHashCode() ^ obj.Property2.GetHashCode());
}
}
You could use a HashSet in the following manner:
var set = new HashSet<MyClass>(new MyClassComparer());
foreach(var myClass in ...)
set.Add(myClass);
Of course, if this definition of equality for MyClass is 'universal', you needn't write an IEqualityComparer implementation; you could just override GetHashCode and Equals in the class itself.
Another point to mention is that you should ensure that your equality function is as you expect. You should override the equals method to set up what properties of your object have to match for two instances to be considered equal.
Then you can just do
mylist.contains(item)
Here is a quick console app to depict the concept of how to solve your issue.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
public class myobj
{
private string a = string.Empty;
private string b = string.Empty;
public myobj(string a, string b)
{
this.a = a;
this.b = b;
}
public string A
{
get
{
return a;
}
}
public string B
{
get
{
return b;
}
}
}
class Program
{
static void Main(string[] args)
{
List<myobj> list = new List<myobj>();
myobj[] objects = { new myobj("a", "b"), new myobj("c", "d"), new myobj("a", "b") };
for (int i = 0; i < objects.Length; i++)
{
if (!list.Exists((delegate(myobj x) { return (string.Equals(x.A, objects[i].A) && string.Equals(x.B, objects[i].B)) ? true : false; })))
{
list.Add(objects[i]);
}
}
}
}
}
Enjoy!
Edit: I had first said:
What's inelegant about the dictionary solution? It seems perfectly elegant to me, especially since you only need to set the comparator in creation of the dictionary.
Of course though, it is inelegant to use something as a key when it's also the value.
Therefore I would use a HashSet. If later operations required indexing, I'd create a list from it when the Adding was done, otherwise, just use the hashset.
Simple but it works
MyList.Remove(nextObject)
MyList.Add(nextObject)
or
if (!MyList.Contains(nextObject))
MyList.Add(nextObject);
A collection can be used as a dictionary, where the difference is that you don't need to a reference to Microsoft Scripting Runtime or use late binding. Please note that in this case the key must be a string. In my case the key(number) is integer, but declared as string.
You can create a custom Boolean function to check if the key exists in the list.
There is a good article be Paul Kelly on ExcelMacroMastery.com
' Function to check if item in the collection already exists
Function Exists(coll As Collection, key As String) As Boolean
On Error GoTo EH
IsObject (coll.Item(key))
Exists = True
EH:
End Function
end you can use it like this
For i = 3 To lastRow
' Ignore the Normal areas
If rg.Cells(i, 1).value <> "Normal" Then
number = rg.Cells(i, 1).value
' Check if the area exist in the collection using a custom function Exists
If Exists(coll, number) = False Then
Set oRiskArea = New clsHighRiskArea
oRiskArea.number = number
coll.add key:=oRiskArea.number, Item:=oRiskArea
Else
Set oRiskArea = coll(number)
End If
With oRiskArea
.name = rg.Cells(i, 2).value
End With
End If
Next i

Categories

Resources