Compare two list for differences - DateTime & String - c#

Sorry but there is no code for this question... I hope thats ok.
So, I have a list of objects with two properties, DateTime and String.
As a matter of fact, DateTime is just date (insertDate), for example 2022-02-04T00:00:00+01:00.
String is of course some text, a lot of text :)
I have to compare the list with these two properties with another list of identical structure.
To be more clear, those are the data returned by the API, so I have to compare is there any differences between these two lists.

Do it like this (it will compare two lists based on element index , so
if your lists are unsorted you must sort them and then use these codes) :
public class YourClass
{
public DateTime DateTime { get; set; }
public string String { get; set; }
}
public class Program
{
static List<YourClass> list1 = new List<YourClass>
{
new YourClass { DateTime = DateTime.MinValue, String = "str1"},
new YourClass { DateTime = DateTime.Now.AddDays(1), String = "str2"},
new YourClass { DateTime = DateTime.Now.AddDays(2), String = "str3"}
};
static List<YourClass> list2 = new List<YourClass>
{
new YourClass { DateTime = DateTime.MinValue, String = "str1"},
new YourClass { DateTime = DateTime.Now.AddDays(-1), String = "2str"},
new YourClass { DateTime = DateTime.Now.AddDays(-2), String = "3str"}
};
static void Main(string[] args)
{
//a list of integer for storing different elements indexes
var differentElementsIndexes = new List<int>();
//check difference of these two list using for loop
for (int i = 0; i < list1.Count; i++)
{
if (list1.ElementAt(i).DateTime != list2.ElementAt(i).DateTime || list1.ElementAt(i).String != list2.ElementAt(i).String)
differentElementsIndexes.Add(i);
}
}

You can use a CustomComparator:
Given your base class for both lists:
public class BaseClass
{
public DateTime date { get; set; }
public string anyString { get; set; }
}
Define the custom comparer for it
public class BaseClassComparer : IEqualityComparer<BaseClass>
{
public bool Equals(BaseClass item1, BaseClass item2)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(item1, item2)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(item1, null) || Object.ReferenceEquals(item2, null))
return false;
//Check whether the required fields' properties are equal.
return (item1.date == item2.date) && (item1.anyString == item2.anyString);
}
public int GetHashCode(BaseClass item)
{
//Check whether the object is null
if (Object.ReferenceEquals(item, null)) return 0;
//Get hash code for the Date field if it is not null.
int hashItemDate = item.date == null ? 0 : item.date.GetHashCode();
//Get hash code for the string field.
int hashItemString = item.anyString.GetHashCode();
//Calculate the hash code for the item.
return hashItemDate ^ hashItemString;
}
}
Then you can use it as follows:
List<BaseClass> class1 = new List<BaseClass>();
List<BaseClass> class2 = new List<BaseClass>();
BaseClass item1 = new BaseClass() {date = DateTime.Now, anyString = "Hello"};
class1.Add(item1);
class2.Add(item1); //<-Equal till here
//class1.Add(item1); //<-uncomment for false
bool result = class1.SequenceEqual(class2, new BaseClassComparer());

Related

Compare 2 Properties of equals object

I have a class with 2 properties
public class SampleClass
{
public string Name { get; set; }
public List<Component> Components { get; set; }
}
And another class which is hold some string properties.
public class Component
{
public string Name { get; set; }
public string Age{ get; set; }
}
I have instance of this class created and added into a List
SampleClass classWithValues = new SampleClass();
var listComponent = new List<Component>();
listComponent.add(new Component{Name = "Random string",Age = "31"})
classWithValues.Components = listComponent;
classWithValues.Name = "TestName"
var listWithObjectClass = new List<SampleClass>();
listWithObjectClass.add(classWithValues);
Then i made a new instance of the SampleClass class and add exactly the same value into the properties :
SampleClass classWithValues1 = new SampleClass();
var listComponent1 = new List<Component>();
listComponent1.add(new Component{Name = "Random string",Age = "31"})
classWithValues1.Components = listComponent1;
classWithValues1.Name = "TestName";
And here is coming the strange part :
if I compare the property Names inside the list with the second instance of the Sample class with the new instance of the same class:
bool alreadyExists = listWithObjectClass.Any(x => x.Name == classWithValues1 .Name);
the result is true BUT
if I compare the List properties
bool alreadyExists = listWithObjectClass.Any(x => x.Components == classWithValues1.Components);
the result is false ?!
Can someone please give some information about this behavior.
Sorry my first answer was not quite right...
In order to get alreadyExist to be true you need to put in place property comparison in your classes as otherwise the equality comparison performed is the default reference comparison. Your objects contains the same property values but are actually different instances... The default equality comparison for objects is comparing references not content.
Try this...
void Main()
{
SampleClass classWithValues = new SampleClass();
var listComponent = new Components();
listComponent.Add(new Component{Name = "Random string",Age = "31"});
classWithValues.Components = listComponent;
classWithValues.Name = "TestName";
var listWithObjectClass = new List<SampleClass>();
listWithObjectClass.Add(classWithValues);
SampleClass classWithValues1 = new SampleClass();
var listComponent1 = new Components();
listComponent1.Add(new Component{Name = "Random string",Age = "31"});
classWithValues1.Components = listComponent1;
classWithValues1.Name = "TestName";
bool alreadyExists = listWithObjectClass.Any(x => x.Components.Equals(classWithValues1.Components));
}
public class SampleClass
{
public string Name { get; set; }
public Components Components { get; set; }
}
public class Component : IEquatable<Component>
{
public string Name { get; set; }
public string Age{ get; set; }
public bool Equals(Component otherComponent)
{
return Name == otherComponent.Name && Age == otherComponent.Age;
}
}
public class Components :List<Component>, IEquatable<Components>
{
public bool Equals(Components otherComponents)
{
if(this.Count!= otherComponents.Count) return false;
return this.TrueForAll(a=> otherComponents.Any(q=>q.Equals(a)))
&& otherComponents.TrueForAll(a=> this.Any(q=>q.Equals(a)));
}
}
The first comparison is about comparing the value of the two string. However, the second comparison is about Comparing two different object which their reference are different. Indeed, for the second comparison, compare their hashCode. To watch this, you can call .GetHashCode() for these two objects.
listComponent.GetHashCode() == listComponent1.GetHashCode() // false
listComponent[0].GetHashCode() == listComponent1[0].GetHashCode() // false

Getting value of next row while iterating through rows

myClass structure :
public class myClass
{
public string Name { get; set; }
public string AdditionalData { get; set; }
public System.DateTime ActivityTime { get; set; }
}
I have a list of the above class List all ordered by ActivityTime say 'acts'.
I wish to convert my data to a list of following class..
public class newClass
{
public string Name { get; set; }
public string AdditionalData { get; set; }
public System.DateTime StartTime { get; set; }
public System.DateTime EndTime { get; set; }
}
Here StartTime will have the same value as the prev class's ActivityTime and so I do not have a problem.
But EndTime should have the ActivityTime value of next list object, this I'm unable to figure how to get..
The last list object's EndTime can be same as starttime
so my code is ..
List<newClass> items = new List<newClass>();
foreach (var item in acts)
{
newClass si = new newClass
{
Name=item.Name,
AdditionalData=item.AdditionalData,
StartTime = item.ActivityTime ,
EndTime = //what do I do here??????
};
items.Add(si);
}
Any help is sincerely appreciated
It's not possible to access the next iterator value until moving to that position.
What you can do here is to remember the previous item and update it in the next loop. Assuming that for the last element it should be empty it will look like this:
List<newClass> items = new List<newClass>();
newClass last = null;
foreach (var item in acts) {
// update the last element here:
if (last != null)
last.EndTime = item.ActivityTime;
newClass si = new newClass
{
Name=item.Name,
AdditionalData=item.AdditionalData,
StartTime = item.ActivityTime ,
//EndTime = null; // will be updated in the next loop
};
last = si;
items.Add(si);
}
// handle the last item (if needed):
if (last != null)
last.EndTime = ... // special value for last item
I have a much elegant solution than for-loop:
List<newClass> output = acts.Select((a, index) => new newClass()
{
Name = a.Name,
AdditionalData = a.AdditionalData,
StartTime = a.ActivityTime,
EndTime = (index + 1 < acts.Count) ? acts[index + 1].ActivityTime : default(DateTime)
}).ToList();
Or you can use a for loop instead of foreach:
List<newClass> items = new List<newClass>();
// assuming length > 0
int length = (acts.Length % 2 == 0) ? acts.Length : acts.Length - 1;
for (int i = 0; i < acts.Length; i++)
{
newClass si = new newClass
{
Name=acts[i].Name,
AdditionalData=acts[i].AdditionalData,
StartTime = acts[i].ActivityTime ,
EndTime = acts[i+1].ActivityTime
};
items.Add(si);
}
if (length < acts.Length)
// handle the last element as you wish
If you want to use LINQ you can create an extension method such as...
public static class EnumerableEx
{
public static IEnumerable<Tuple<T, T>> WithNext<T>(this IEnumerable<T> items)
{
var e = items.GetEnumerator();
bool r = e.MoveNext();
if (!r)
yield break;
do
{
T last = e.Current;
var item = (r = e.MoveNext()) ? e.Current : default(T);
yield return Tuple.Create(last, item);
} while (r);
}
}
... here is an example on how to use it ...
class Program
{
static void Main(string[] args)
{
var i = new int?[] { 1, 2, 3 };
foreach (var n in i.WithNext())
//the last value will be paired with a null.
// you can use null coalesce to fill in the missing item.
Console.WriteLine("{0} => {1}", n.Item1, n.Item2 ?? 9);
/*
1 => 2
2 => 3
3 => 9
*/
}
}

Convert Array to custom object list c#

I have a array:-
private string[][] barValues = new string[][] { new string[]{ "1.9", "5.8", "4.8", "Since Inception", "24-Jan 2014 to 24 Jun 2014" },
new string[]{"1.2", "16.5","9.8", "Year to date","01-Apr 2014 to 24-Jun 2014" },
new string[]{"11.6","28.8","23.5","Last quarter","01-Jan to 24-Jun 2014"} };
I want to convert this array into my custom list :-
List<Portfolio> list = new List<Portfolio>();
I tried doing :-
List<Portfolio> list=myArray.Cast<Portfolio>().ToList();
But I get a error:-
System.InvalidCastException: Cannot cast from source type to
destination type.
How do I do this conversion?
You will need to use the Select operator and assign your array of strings to your Portfolio object. Something like this:
myArray.Select(array => new Portfolio { Field1 = array[0], Field2 = array[1] }).ToList()
There is no "magic" conversion from string[] to your class PortFolio, you have to do it manually.
This could work:
List<Portfolio> portFolioList = barValues
.Select(sArr => new Portfolio
{
Values = sArr.Take(3).Select(double.Parse).ToList(),
Name = sArr.Skip(3).First(),
TimeSpan = sArr.Last()
}).ToList();
If you have a class like this:
public class Portfolio
{
public List<double> Values { get; set; }
public string Name { get; set; }
public string TimeSpan { get; set; }
}
Convert Array to string try this way
string[] arr = ...
List<object> list= new List<object>(arr);
foreach(object obj in arr)
list.add(obj);
var converted = barValues.Select(a => new {Value1 = a[0], Value2= a[1]}).ToArray();
This gives you an array of anonymous objects. Just replace my anonymous constructor with your constructor of Portfolio. Lambda variable a is the array that contains the string values.
Create a constructor taking the items from barValues and iterator over those.
like:
class PortFolio()
{
PortFolio(string e1, string e2, string e3, string period, string date)
{
// Copy to properties.
}
...
}
Then in the code for copying:
foreach (var barValue in barValues)
{
list.Add(new PortFolio(barValue[0], barValue[1], barValue[2], barValue[3], barValue[4]); }
}
C# is a strongly typed language. There is no out of the box way to magically cast a bunch of string into an object.
A correct way to do what you want is to first add a constructor on your Portfolio class that takes an array of strings and assign its values to your instance properties:
class Portfolio
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
public string Method { get; set; }
public string Period { get; set; }
public Portfolio(string[] values)
{
if (values != null)
{
this.Value1 = values.ElementAtOrDefault(0);
this.Value2 = values.ElementAtOrDefault(1);
this.Value3 = values.ElementAtOrDefault(2);
this.Method = values.ElementAtOrDefault(3);
this.Period = values.ElementAtOrDefault(4);
}
}
}
You can then use linq to build your list :
var portfoliosList = barValues.Select(values => new Portfolio(values)).ToList();
If required, you can do additional work in the constructor, for instance converting the values to decimal, the method to an Enum and the period as a couple of DateTime.
If you cannot modify the Portfolio class, because it's a third party component or out of your scope, you can create a factory class with a method containing that logic :
static class ProtfolioFactory
{
static public Portfolio BuildPortfolio(string[] values)
{
var portfolio = new Portfolio();
if (values != null)
{
portfolio.Value1 = values.ElementAtOrDefault(0);
portfolio.Value2 = values.ElementAtOrDefault(1);
portfolio.Value3 = values.ElementAtOrDefault(2);
portfolio.Method = values.ElementAtOrDefault(3);
portfolio.Period = values.ElementAtOrDefault(4);
}
return portfolio;
}
}
The linq query then becomes :
var list = barValues.Select(values => ProtfolioFactory.BuildPortfolio(values)).ToList();

c# how to retrive objects as values from array

I am trying to create a simple 'inventory' system that stores items with the key being an items name, and with the remaining information being stored as a value. However, I am having difficulty figuring out how to then read the information. For example, if I have say a list of 10 items, and I want to select the items 'type' information from the key 'television' outlined below, how could I do this?
television {large, 5, 3, false, dynamic, 0.8, 20}
Hashtable myItems = new Hashtable();
protected virtual bool OnAttempt_AddItem(object args) {
object[] arr = (object[])args;
string ItemType = (string)arr[0];
string ItemName = (string)arr[1];
int ItemAmount = (arr.Length == 2) ? (int)arr[2] : 1;
int ItemACanHave = (arr.Length == 3) ? (int)arr[3] : 1;
bool ItemClear = (bool)arr[4];
string ItemEffect = (string)arr[5];
float ItemModifier = (float)arr[6];
int ItemWeight = (int)arr[7];
// enforce ability to have atleast 1 item of each type
ItemACanHave = Mathf.Max(1, ItemACanHave);
myItems[ItemName] = new object[] {ItemType, ItemAmount, ItemACanHave, ItemClear, ItemEffect, ItemModifier, ItemWeight };
return true;
}
Create an item class to encapsulate the properties:
public class InventoryItem
{
public string Name;
public string Type;
public int Amount;
public int CanHave; // you should consider renaming this - it's very unclear what this could mean
public bool Clear;
public string Effect;
public float Modifier;
public int Weight;
}
Then you can use a Dictionary to store items:
Dictionary<string, InventoryItem> inventory = new Dictionary<string, InventoryItem>();
inventory["television"] = new InventoryItem
{
Name = "television", Type = "large", Amount = 5,
CanHave = 3, Clear = false, Effect = "dynamic",
Modifier = 0.8, Weight = 20
});
And you can look it up like this:
Console.WriteLine("Type of television is: ", inventory["television"].Type);
I would suggest you to consider the possibility of more than one item of a certain type in a inventory list, i.e. two or more television sets instead of only one.
Use a base class and derived classes:
public class InventoryItem
{
public string ItemType { get; set; }
public string ItemName { get; set; }
public int ItemAmount { get; set; }
public int ItemACanHave { get; set; }
public bool ItemClear { get; set; }
public string ItemEffect { get; set; }
public float ItemModifier { get; set; }
public int ItemWeight { get; set; }
}
public class Radio : InventoryItem
{
}
public class Television : InventoryItem
{
}
// TODO: add your derived classes
Use a List<InventoryItem> to store the collection:
List<InventoryItem> InventoryItems = new List<InventoryItem>();
Modify your method (don't forget to add exception handling, as sometimes you might get different input than the one you expected in the args object):
protected virtual bool OnAttempt_AddItem(object args)
{
// TODO: handle unboxing exceptions, size of the array etc
//
try
{
object[] arr = (object[])args;
switch (arr[0].ToString().ToLower())
{
// TODO: add other types (Radio etc)
case "television":
var tv = new Television();
tv.ItemType = (string)arr[0];
tv.ItemName = (string)arr[1];
tv.ItemAmount = (arr.Length == 2) ? (int)arr[2] : 1;
tv.ItemACanHave = (arr.Length == 3) ? (int)arr[3] : 1;
tv.ItemClear = (bool)arr[4];
tv.ItemEffect = (string)arr[5];
tv.ItemModifier = (float)arr[6];
tv.ItemWeight = (int)arr[7];
// enforce ability to have atleast 1 item of each type
tv.ItemACanHave = Math.Max(1, tv.ItemACanHave);
InventoryItems.Add(tv);
break;
default:
var genericItem = new InventoryItem();
genericItem.ItemType = (string)arr[0];
genericItem.ItemName = (string)arr[1];
genericItem.ItemAmount = (arr.Length == 2) ? (int)arr[2] : 1;
genericItem.ItemACanHave = (arr.Length == 3) ? (int)arr[3] : 1;
genericItem.ItemClear = (bool)arr[4];
genericItem.ItemEffect = (string)arr[5];
genericItem.ItemModifier = (float)arr[6];
genericItem.ItemWeight = (int)arr[7];
// enforce ability to have atleast 1 item of each type
genericItem.ItemACanHave = Math.Max(1, genericItem.ItemACanHave);
InventoryItems.Add(genericItem);
break;
//handle other cases
}
return true;
}
catch (Exception ex)
{
// log the error
return false;
}
}
Retrieve the filtered items like this:
var largeTvType = inventory.InventoryItems.OfType<Television>()
// filter by type (or other criteria)
.Where(tv => tv.ItemType == "large")
// select only the property your interested in (in the case below
// it will be always "television" because that's part of the
// logic inside the OnAttempt_AddItem method's switch statement)
.Select(tv => tv.ItemType);
Still, as ChrisWue suggested in his answer, if you know that your inventory lists will be very large, I'd recommend you to use a Dictionary<string, InventoryItem>, the string key being a unique inventory item identifier. It will be faster.

C# How to create a list of classes

I am new to c#, but not to programming. First of, thank you so much for your help!
I want a class or struct whichever is appropriate which has 3 variables. A string, and two datetimes.
I want to create a loop which stores new classes in a list.
Something like:
for each item in dataViewer
create new class
assign variables
store class in list
next
Thank you so much for your help
You can do this easily with LINQ:
var list = dataViewer
.Select(item => new YourClass
{
StringProperty = ...,
DateTimeProperty1 = ...,
DateTimeProperty2 = ...
})
.ToList();
It lets you state your intentions (create a list of YourClass objects from each item in dataViewer) without emphasizing the mechanics behind it (loops, etc.)
Edit: If you don't require a list, just a sequence, this also looks nice using the query syntax (same meaning):
var yourClasses =
from item in dataViewer
select new YourClass
{
StringProperty = ...,
DateTimeProperty1 = ...,
DateTimeProperty2 = ...
};
Maybe something like this
var list = new List<YourClass>();
foreach(var item in dataViewer) {
var cls = new YourClass();
// Assign variables here
// cls.Test = item.Test;
list.Add(cls);
}
Try this:
public class YourClass
{
public string YourString {get; set;}
public DateTime YourDate1 {get; set;}
public DateTime YourDate2 {get; set;}
public YourClass(string s, DateTime d1, DateTime d2)
{
YourString = s;
YourDate1 = d1;
YourDate2 = d2;
}
}
public List<YourClass> Read()
{
List<YourClass> list = new List<YourClass>();
foreach(var item in dataViewer)
list.Add(new YourClass(s,d1,d2)); // Read variables from item...
return list;
}
public class Appropriate
{
public string Value { get; set; }
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
IList<Appropriate> list = new List<Appropriate>();
foreach(var item in dataViewer) {
list.Add(new Appropriate() {
Value = item["value"],
Start = item["start"],
End = item["end"]
});
}
IList<Appropriate> list = new List<Appropriate>();
dataViewer.ToList().ForEach(i => list.Add(new Appropriate() {
Value = item["value"],
Start = item["start"],
End = item["end"]
});
public class Foo
{
public Foo(string name, DateTime dt1, DateTime dt2)
{
Name = name;
DT1 = dt1;
DT2 = dt2;
}
public string Name { get; set; }
public DateTime DT1 { get; set; }
public DateTime DT2 { get; set; }
}
public class Example
{
public List<Foo> example(DataView dataViewer)
{
var foos = new List<Foo>();
foreach(var data in dataViewer)
{
foos.Add(new Foo(data.Name, data.DT1, data.DT2);
}
return foos;
}
}

Categories

Resources