C# Refactoring the same action with different details using design patterns - c#

I try to find the way for refactoring my code but no idea how to do this.
For example, we have several classes
class A {
string name;
int year;
}
class B {
long id;
string code;
DateTime currentTime;
}
class C {
string lastname;
DateTime currentDate;
}
And latter I need to return list of objects of these classes List, List, List and convert them into Object[][].
For every conversion I do the same
private Object[][] converAListToObjectArray(List<A> dataList)
{
long countRecords = dataList.Count();
const int countProperty = 2;
var arrayRes = new object[countRecords][];
for (int i = 0; i < countRecords; i++)
{
var arrayObjProperty = new object[countProperty];
arrayObjProperty[0] = dataList[i].Name;
arrayObjProperty[1] = dataList[i].Year;
arrayRes[i] = arrayObjProperty;
}
return arrayRes;
}
private Object[][] converBListToObjectArray(List<B> dataList)
{
long countRecords = dataList.Count();
const int countProperty = 3;
var arrayRes = new object[countRecords][];
for (int i = 0; i < countRecords; i++)
{
var arrayObjProperty = new object[countProperty];
arrayObjProperty[0] = dataList[i].Id;
arrayObjProperty[1] = dataList[i].Code;
arrayObjProperty[2] = dataList[i].CurrentTime;
arrayRes[i] = arrayObjProperty;
}
return arrayRes;
}
Is it possible separate this convertion using some design pattern?

You could write a generic function which uses reflection to get each object's field names and values. You'd need to decide whether it should work for public or private fields. The example below grabs both the public and the private fields:
static object[][] ConvertToObjectArray<T>(IList<T> objects)
{
var fields = (from fieldInfo in typeof(T).GetFields(
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
orderby fieldInfo.Name
select fieldInfo).ToArray();
object[][] table = new object[objects.Count][];
for (int i = 0; i < table.Length; i++)
{
table[i] = (from fieldInfo in fields
select fieldInfo.GetValue(objects[i])).ToArray();
}
return table;
}

You could use an action and do something like this: -
class Program
{
// Your new function, (doesn't have to be static; just did it for the demo)
// If you really really want to return object[][] still,
// You'll need to pass an index to foo as well
private static List<IList<object>> convert<T>(IList<T> dataList, Action<IList<object>, T> foo)
{
var arrayRes = new List<IList<object>>();
foreach (var item in dataList)
{
var arrayObjProperty = new List<object>();
foo(arrayObjProperty, item);
arrayRes.Add(arrayObjProperty);
}
return arrayRes;
}
// The rest is just calling the function with two examples
static void Main(string[] args)
{
var bar = new List<A>();
bar.Add(new A() { name = "qux", year = 2013 });
var objects1 = convert(bar, (a, b) =>
{
a.Add(b.name);
a.Add(b.year);
});
var baz = new List<B>();
baz.Add(new B() { code = "qux", id = 2013 });
var objects2 = convert(baz, (a, b) =>
{
a.Add(b.code);
a.Add(b.id);
});
}
}
You can just copy this into your IDE to have a play and see how it works. Basically, this uses generics and then an action to allow you to do the only part that differs each time in a lambda that is passed to the method.

You could simplify and use something like this - e.g. for B...
List<B> list = new List<B>
{
new B{ id = 1, code = "", currentTime = DateTime.Now},
new B{ id = 1, code = "", currentTime = DateTime.Now},
new B{ id = 1, code = "", currentTime = DateTime.Now},
};
var array = list.Select(x => new object[] { x.id, x.code, x.currentTime }).ToArray();

Your Object[][] structure is a little unusual to my eye. Did you know there's a Cast method for lists?
You can do the following:
List<A> foo = new List<A> ();
// initialize foo here
var bar = foo.Cast<Object>().ToArray();
Which will get you a an array of objects with your field names intact. If you absolutely need to have the fields as array elements (why do you want this) you could add a ToObject method to each of your classes:
class A
{
public string name;
public int year;
public Object[] ToObject()
{
return new Object[] {name, year};
}
}
List<A> foo = new List<A>
{
new A{name="reacher",year=2013},
new A{name="Ray",year=2013}
};
var bux = foo.Select(a => a.ToObject()).ToArray() ;

Related

Generic Converters for struct to class and vice versa

I want to have two converters like these:
public class PacMan<T2> where T2 : new()
{
public static List<T1> ArrayToList<T1>(T2[] array)
{
var list = new List<T1>(array.Length);
for (int i = 0; i < array.Length; i++) list.Add(array[i]);
return list;
}
public static T2[] ListToArray<T1>(List<T1> list)
{
var array = new T2[list.Count];
for (int i = 0; i < list.Count; i++) array[i] = list[i];
return array;
}
}
where T1 is a class and T2 is a struct. Both the class and struct members have Identical names and types. With the above I get red squigly in first method's list.Add(array[i]) and second methods array[i] = list[i] so these don't work. What's the easiest way to do that?
EDIT
Here's the class:
public class PerSec : INotifyPropertyChanged
{
string yq;
float eps, nav, cash, debt;
public string YQ { get => yq; set { yq = value; OnPropertyChanged(); } }
public float EPS { get => eps; set { eps = value; OnPropertyChanged(); } }
public float NAV { get => nav; set { nav = value; OnPropertyChanged(); } }
public float Cash { get => cash; set { cash = value; OnPropertyChanged(); } }
public float Debt { get => debt; set { debt = value; OnPropertyChanged(); } }
#region Notify Property Changed Members
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string name = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
#endregion
}
and here's the struct:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PerSecStruct
{
//23 bytes
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)]
public string YQ;
public float EPS;
public float NAV;
public float Cash;
public float Debt;
}
EDIT
In second method, I've these now:
public static T2[] ListToarray<T1>(List<T1> list)
{
var structFields = typeof(PerSecStruct).GetFields(BindingFlags.Instance | BindingFlags.Public);
var classFields = typeof(PerSec).GetProperties(BindingFlags.Instance | BindingFlags.Public);
classFields = classFields.Where(x => structFields.Select(y => y.Name).Contains(x.Name)).ToArray();
var fieldsDictionary = structFields.Zip(classFields, (k, v) => new { StructField = k, ClassField = v }).ToDictionary(x => x.StructField, x => x.ClassField);
var array = new T2[list.Count];
for (int i = 0; i < list.Count; i++)
{
var psStruct = array[i];
var psClass = list[i];
foreach (var entry in fieldsDictionary)
{
var value = entry.Value.GetValue(psClass);
entry.Key.SetValue(psStruct, value);
}
}
return array;
}
this entry.Key.SetValue(psStruct, value); line isn't working so the elements of array have their default values (null/0).
EDIT
It works if I use __makeref(array[i]) as noted here by petelids. With that I can do this:
public static T2[] ListToarray<T1>(List<T1> list)
{
var fields = typeof(T2).GetFields();
var properties = typeof(T1).GetProperties();
var array = new T2[list.Count];
for (int i = 0; i < list.Count; i++)
{
foreach (var field in fields)
{
var value = properties.First(x => x.Name == field.Name).GetValue(list[i]);
field.SetValueDirect(__makeref(array[i]), value);
}
}
return array;
}
I don't need those Binding Flags! And to convert back to List I've to do this in other method:
public static List<T1> ArrayToList<T1>(T2[] array) where T1 : new()
{
var fields = typeof(T2).GetFields();
var properties = typeof(T1).GetProperties();
var list = new List<T1>(array.Length);
for (int i = 0; i < array.Length; i++)
{
var obj = new T1();
foreach (var property in properties)
{
var value = fields.First(x => x.Name == property.Name).GetValue(array[i]);
property.SetValue(obj, value);
}
list.Add(obj);
}
return list;
}
You could use most of serializers for that, for example with Json.NET:
using Newtonsoft.Json;
...
internal static class MyConverter
{
internal static T Convert<T>(object source)
{
string json = JsonConvert.SerializeObject(source);
T result = JsonConvert.DeserializeObject<T>(json);
return result;
}
}
usage:
var s1 = new PerSecStruct { YQ = "1", EPS = 2, NAV = 3, Cash = 4, Debt = 5 };
// to object
var o = MyConverter.Convert<PerSec>(s1);
// back to struct
var s2 = MyConverter.Convert<PerSecStruct>(o);
This can be done using Reflection, although I also highly advise you look at AutoMapper as Knoop pointed out in the comments, but if you only need it for this one struct you could code your own implementation.
Reflection "is the ability of a process to examine, introspect, and modify its own structure and behaviour", in C# we use the System.Reflection namespace for this.
So we want to map all public fields of one struct to all public properties of another, for this we first need to get all public fields on our struct and all the properties on our class like so:
(For this we need an instance of each)
var psStruct = ...;
var psClass =...;
// BindingFlags.Instance means all instance fields (non-static)
// BindingFlags.Public means only public fields
var structFields = typeof(PerSecStruct).GetFields(BindingFlags.Instance | BindingFlags.Public);
// The BindingFlags are the same, but we use 'GetProperties' because you declared properties not fields
var classFields = typeof(PerSec).GetProperties(BindingFlags.Instance | BindingFlags.Public);
So now we have a list of both the fields on the struct and the Properties on the class, so we can actually begin mapping:
// First filter the list on the Class to make sure we only have properties the struct has as well
classFields = classFields.Where(x => structFields.Select(y => y.Name).Contains(x.Name));
// ToDictionary to combine both lists into one
var fieldsDictionary = structFields.Zip(classFields, (k, v) => new {StructField = k, ClassField = v}).ToDictionary(x => x.StructField, x => x.ClassField);
foreach (var entry in fieldsDictionary)
{
// Get the value from the psStruct object
var value = entry.Key.GetValue(psStruct);
// Set value on the psClass object (this can be a one-liner but I prefer this as it is more readable)
entry.Value.SetValue(psClass, value);
}
A concrete example of how to use this:
public static List<TTarget> As<TSource>(IEnumerable<TSource> source) where TTarget : new();
{
var sourceFields = typeof(TSource).GetFields(BindingFlags.Instance | BindingFlags.Public);
var targetProperties = typeof(TTarget).GetProperties(BindingFlags.Instance | BindingFlags.Public);
var mapping = sourceFields.Zip(targetProperties, (k, v) => new {Source = k, Target = v}).ToDictionary(x => x.Source, x => x.Target);
var retval = new List<TTarget>();
foreach (var sourceObject in source)
{
var mappedObject = new TTarget();
foreach (var m in mapping)
{
var value = entry.Key.GetValue(sourceObject);
entry.Value.SetValue(mappedObject, value);
}
retval.Add(mappedObject);
}
return retval;
}
I added an OrderBy clause to getting the struct/ class fields/ Properties because else the order or declaration inside the struct/ class would have had to have been the same, like this it doesn't matter.
This currently does not work both ways, but if you decide to replace the fields inside your struct with Properties you can replace the typeof(TSource).GetFields(...) with typeof(TSource).GetProperties(...), then it would work both ways

C# Calculate Combinations from Multiple Lists of Custom Objects with No Repetition

So I have been struggling to find a way to create all combinations with no repetition from multiple lists containing custom objects. There are some additional constraints that make it a bit more challenging, of course.
Basically I am parsing a bunch of data from a .csv file that contains part information. This data is then passed on to a custom object and then those objects are added to lists based on their "group." (See code below)
So once the information has been parsed I now have 6 lists containing any number of elements. Now I need to generate all combinations between those 6 lists following these rules:
One object from groupA
Two objects from groupB (no repetition)
Three objects from groupC (no repetition)
One object from groupD
One object from groupE
One object from groupF
These objects are then used to create a ModuleFull object, and my overall end result should be a List<ModuleFull> containing all the combinations generated from the part lists.
I was able to figure out a way to do this using LINQ although I did not test it using lists of custom objects because I realized my lists all contain different numbers of elements.
So any help that I could get in coming up with a method to solve this using recursion would be greatly appreciated.
Here is the code parsing the data:
using (TextFieldParser parser = new TextFieldParser(#"c:\temp\test.csv"))
{
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(",");
while (!parser.EndOfData)
{
string[] fields = parser.ReadFields();
Part tempPart = new Part(fields[0], fields[2], fields[1], double.parse(fields[4]), long.parse(fields[3]));
allParts.Add(tempPart);
if (tempPart.group == "A")
{
aParts.Add(tempPart);
}
else if (tempPart.group == "B")
{
bParts.Add(tempPart);
}
else if (tempPart.group == "C")
{
cParts.Add(tempPart);
}
else if (tempPart.group == "D")
{
dParts.Add(tempPart);
}
else if (tempPart.group == "E")
{
eParts.Add(tempPart);
}
else if (tempPart.group == "F")
{
fParts.Add(tempPart);
}
}
Below are the two classes for the objects that fill the lists:
public class Part
{
public string idNum; //0 locations when being parsed
public string name; //2
public string group; //1
public double tolerance; //4
public long cost; //3
public Part(string id, string nm, string grp, double tol, long cst)
{
idNum = id;
name = nm;
group = grp;
tolerance = tol;
cost = cst;
}
}
public class ModuleFull
{
public Part groupA;
public Part groupBOne;
public Part groupBTwo;
public Part groupCOne;
public Part groupCTwo;
public Part groupCThree;
public Part groupD;
public Part groupE;
public Part groupF;
public ModuleFull(Part a, Part b1, Part b2, Part c1, Part c2, Part c3, Part d, Part e, Part f)
{
groupA = a;
groupBOne = b1;
groupBTwo = b2;
groupCOne = c1;
groupCTwo = c2;
groupCThree = c3;
groupD = d;
groupE = e;
groupF = f;
}
}
The code below uses a custom enumerator to get unique combinations. Very clean solution.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace IEnumerable_IEnumerator_Recursive
{
class Program
{
const string FILENAME = #"c:\temp\test.csv";
static void Main(string[] args)
{
Parser parser = new Parser(FILENAME);
int level = 0;
List<Part> root = new List<Part>();
Part.Recurse(level, root);
}
}
public class Parser
{
public Boolean EndOfData = false;
public Parser(string filename)
{
StreamReader reader = new StreamReader(filename);
string inputLine = "";
while ((inputLine = reader.ReadLine()) != null)
{
inputLine = inputLine.Trim();
if (inputLine.Length > 0)
{
string[] fields = inputLine.Split(new char[] { ',' });
Part tempPart = new Part(fields[0], fields[1], fields[2], fields[3], fields[4]);
Part.allParts.Add(tempPart);
}
}
Part.MakeDictionary();
}
}
public class PartEnumerator : IEnumerator<List<Part>>
{
List<Part> parts = null;
public static SortedDictionary<string, int> maxCount = new SortedDictionary<string, int>() {
{"A", 1},
{"B", 2},
{"C", 3},
{"D", 1},
{"E", 1},
{"F", 1}
};
public int size = 0;
List<int> enumerators = null;
public PartEnumerator(string name, List<Part> parts)
{
this.parts = parts;
size = maxCount[name];
enumerators = new List<int>(new int[size]);
Reset();
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public List<Part> Current
{
get
{
try
{
List<Part> returnParts = new List<Part>();
foreach (int enumerator in enumerators)
{
returnParts.Add(parts[enumerator]);
}
return returnParts;
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public void Reset()
{
for (int count = 0; count < enumerators.Count; count++)
{
enumerators[count] = count;
}
}
public Boolean MoveNext()
{
Boolean moved = true;
int listSize = parts.Count;
int enumNumbers = enumerators.Count;
//only use enumerators up to the size of list
if (listSize < enumNumbers)
{
enumNumbers = listSize;
}
Boolean ripple = true;
int enumCounter = enumNumbers;
if (enumCounter > 0)
{
while ((ripple == true) && (--enumCounter >= 0))
{
ripple = false;
int maxCount = listSize - (enumNumbers - enumCounter);
if (enumerators[enumCounter] >= maxCount)
{
ripple = true;
}
else
{
for (int i = enumCounter; i < enumNumbers; i++)
{
if (i == enumCounter)
{
enumerators[i] += 1;
}
else
{
enumerators[i] = enumerators[i - 1] + 1;
}
}
}
}
if ((enumCounter <= 0) && (ripple == true))
{
moved = false;
}
}
return moved;
}
public void Dispose()
{
}
}
public class Part
{
public static List<Part> allParts = new List<Part>();
public static Dictionary<string, PartEnumerator> partDict = new Dictionary<string, PartEnumerator>();
public string idNum; //0 locations when being parsed
public string name; //2
public string group; //1
public double tolerance; //4
public long cost; //3
public Part()
{
}
public Part(string id, string nm, string grp, string tol, string cst)
{
idNum = id;
name = nm;
group = grp;
tolerance = double.Parse(tol);
cost = long.Parse(cst);
}
public static void MakeDictionary()
{
var listPartEnum = Part.allParts.GroupBy(x => x.name)
.Select(x => new { Key = x.Key, List = new PartEnumerator(x.Key, x.ToList()) });
foreach (var partEnum in listPartEnum)
{
partDict.Add(partEnum.Key, partEnum.List);
}
}
public static string[] NAMES = { "A", "B", "C", "D", "E", "F" };
public static void Recurse(int level, List<Part> results)
{
Boolean moved = true;
if (level < PartEnumerator.maxCount.Keys.Count)
{
//level is equivalent to names in the Part Enumerator dictionary A to F
string name = NAMES[level];
PartEnumerator enumerator = partDict[name];
enumerator.Reset();
while ((enumerator != null) && moved)
{
List<Part> allParts = new List<Part>(results);
allParts.AddRange((List<Part>)enumerator.Current);
int currentLevel = level + 1;
Recurse(currentLevel, allParts);
moved = enumerator.MoveNext();
}
}
else
{
string message = string.Join(",", results.Select(x => string.Format("[id:{0},name:{1}]", x.name, x.idNum)).ToArray());
Console.WriteLine(message);
}
}
}
}
I used following input file
1,A,X,0,0
2,A,X,0,0
3,A,X,0,0
4,A,X,0,0
5,A,X,0,0
1,B,X,0,0
2,B,X,0,0
3,B,X,0,0
4,B,X,0,0
5,B,X,0,0
1,C,X,0,0
2,C,X,0,0
3,C,X,0,0
4,C,X,0,0
5,C,X,0,0
1,D,X,0,0
2,D,X,0,0
3,D,X,0,0
4,D,X,0,0
5,D,X,0,0
1,E,X,0,0
2,E,X,0,0
3,E,X,0,0
4,E,X,0,0
5,E,X,0,0
1,F,X,0,0
2,F,X,0,0
3,F,X,0,0
4,F,X,0,0
5,F,X,0,0
This can be solved with two related methods. One is a method to generate all combinations of items from a list. This deals with your cases where you want more than one from a set, like group B and C. The other is a method to get you all the ways to combine one element from each list, which is otherwise known as a Cartesian product and is, in a way, a special case of the first method.
I recently wrote a library of combinatorial functions that includes both of these, so I can share my implementation with you. My library is on Github if you want to look at the source code, and can be installed from NuGet if you'd like. (The examples below are slightly simplified to fit your situation; in my fuller versions, the combinations method has different modes that allow you to specify whether it matters what order the output items are in, and whether it's allowed to use source items more than once. Neither are needed here, so they've been omitted.)
So, the first of those methods looks something like this:
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> source, int combinationSize)
{
if (combinationSize > source.Count())
{
return new List<IEnumerable<T>>();
}
if (source.Count() == 1)
{
return new[] { source };
}
var indexedSource = source
.Select((x, i) => new
{
Item = x,
Index = i
})
.ToList();
return indexedSource
.SelectMany(x => indexedSource
.OrderBy(y => x.Index != y.Index)
.Skip(1)
.OrderBy(y => y.Index)
.Skip(x.Index)
.Combinations(combinationSize - 1)
.Select(y => new[] { x }.Concat(y).Select(z => z.Item))
);
}
The second method is from a blog post by Eric Lippert (which was actually inspired by another StackOverflow question), and looks like this:
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
if (sequences == null)
{
throw new ArgumentNullException(nameof(sequences));
}
IEnumerable<IEnumerable<T>> emptyProduct = new IEnumerable<T>[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] { item }))
.Where(x => x.Any());
}
The two methods can be combined like this:
var groupA = new[] { "a", "aa", "aaa", "aaaa", "aaaaa" };
var groupB = new[] { "b", "bb", "bbb", "bbbb", "bbbbb" };
var groupC = new[] { "c", "cc", "ccc", "cccc", "ccccc" };
var groupD = new[] { "d", "dd", "ddd", "dddd", "ddddd" };
var groupE = new[] { "e", "ee", "eee", "eeee", "eeeee" };
var groupF = new[] { "f", "ff", "fff", "ffff", "fffff" };
var options = new[]
{
groupA.Combinations(1), // One object from groupA
groupB.Combinations(2), // Two objects from groupB (no repetition)
groupC.Combinations(3), // Three objects from groupC (no repetition)
groupD.Combinations(1), // One object from groupD
groupE.Combinations(1), // One object from groupE
groupF.Combinations(1) // One object from groupF
};
return options.CartesianProduct();
So, we generate the various ways of satisfying each of your sub-conditions first: one from this group, two from that group, etc. Then, we look at all ways of putting those together to form a group of subgroups. The result is an IEnumerable<IEnumerable<T>> where T is the type of what you started with - in this case, string, but for you it could be something else. You can then iterate over this and use each set to build your result type.
Be aware that, like many combinatorial problems, this can scale quite fast. For example, with my test data this returns 62,500 possible combinations.

C# - Representing multiple arrays as a List<Tuple<x,x,x,x,>>

We have an old framework called CAGen in which legacy mainframe devs create "CABS" and then .NET code is generated to allow consumption in more modern languages. Unfortunately it seems to take thew rows of results from the underlying database and rather than create an object with properties its "groups" are generated as multiple arrays (one per column) all of equal length along with a Length value so we know how many rows to read.
Rather than manually handle reach column I'd like to build some kind of wrapper class which takes all the arrays and exposes them as if they were a List. I'd like it to be compile safe and was thinking using Tuples. Rather than start from scratch I'm wondering if anyone knows of a neat approach or existing component that may help ease the burden?
UPDATE: Definitive answer
Thanks to Linq and some insight from a colleague it's a one liner to wrap any number of arrays onto a model for easy iteration with a foreach:
int[] ids = new int[5] { 5, 3, 2, 1, 4 };
string[] names = new string[5] { "John", "Joan", "Bob", "Mark", "Someone" };
bool[] bools = new bool[5] { true, true, false, true, false };
var items = ids.ToList().Select(
(id, index) =>
new
{
ID = id,
Name = names[index],
YesOrNo = bools[index]
});
==========================================================
Thanks for the idea Gary.
Here's what I came up with:
public class MultiListWrapper : List<object[]>
{
private Type[] fieldTypes;
public MultiListWrapper(int length, params object[] fields)
{
fieldTypes = fields.Select(f => f.GetType()).ToArray();
for (int r = 0; r < length; r++)
{
var row = new object[fields.Length];
for (int f = 0; f < fields.Length; f++)
{
var field = fields[f];
row[f] = (field as Array).GetValue(r);
}
Add(row);
}
}
}
As it is the underlying values once instantiated still do show up as type object but I'm thinking that I could still provide a property or IEnumerable< TModel > Get< TModel >() method would would use the fieldTypes array as validation / casting to the requested model for each row and perhaps rather than subclass a List, just use compostition and hold the list internally to force casting for the consumer.
At any rate, it seems like two lines of code now to wrap the multiple arrays to a nice object which can be enumerated and used like any old Enumerable POCO.
========================================
Edit:
So I've finished off the implementation, thought I'd post it here in case anyone was interested:
public class MultiListWrapper<T> : IEnumerable<T>
where T : class, new()
{
private List<object[]> list = new List<object[]>();
private Type[] fieldTypes;
private int length = 0;
public MultiListWrapper(int length, params object[] fields)
{
this.length = length;
fieldTypes = fields.Select(f => f.GetType()).ToArray();
for (int r = 0; r < length; r++)
{
var row = new object[fields.Length];
for (int f = 0; f < fields.Length; f++)
{
var field = fields[f];
row[f] = (field as Array).GetValue(r);
}
list.Add(row);
}
}
public int Count { get { return length; } }
public T this[int index]
{
get
{
if (index >= length)
throw new IndexOutOfRangeException();
// Create the return item
var item = new T();
var properties = item.GetType().GetProperties();
// Get fields from list item
var listItem = list[index];
for (int i = 0; i < listItem.Length; i++)
{
if(properties[i].PropertyType.FullName != fieldTypes[i].FullName.Replace("[]", string.Empty))
throw new SettingsPropertyWrongTypeException(
string.Format("The type for Property '{0}' on the requested model '{1}' doesn't match the underlying list item property type",
properties[i].Name,
typeof(T).FullName));
properties[i].SetValue(item, listItem[i]);
}
return item;
}
}
private MultiListWrapperEnumerator<T> _enumerator;
public IEnumerator<T> GetEnumerator()
{
if (_enumerator == default(MultiListWrapperEnumerator<T>))
{
_enumerator = new MultiListWrapperEnumerator<T>(this);
}
return _enumerator;
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#region Enumerator for the wrapper
private class MultiListWrapperEnumerator<T> : IEnumerator<T>
where T : class, new()
{
private MultiListWrapper<T> _listWrapper;
private int _index;
public MultiListWrapperEnumerator(MultiListWrapper<T> listWrapper)
{
_listWrapper = listWrapper;
_index = -1;
}
public void Dispose()
{
}
public bool MoveNext()
{
_index++;
if (_index >= _listWrapper.Count)
return false;
return true;
}
public void Reset()
{
_index = -1;
}
public T Current
{
get { return _listWrapper[_index]; }
private set { throw new NotImplementedException(); }
}
object IEnumerator.Current
{
get { return Current; }
}
}
#endregion
}
To use it you create you various arrays (all the same length), create a POCO model to represent "rows" against the array elements then pass them all into the wrapper:
public class TheModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool YesOrNo { get; set; }
}
int[] ids = new int[5] { 1, 2, 3, 4, 5 };
string[] names = new string[5] { "John", "Joan", "Bob", "Mark", "Someone" };
bool[] bools = new bool[5] { true, true, false, true, false };
var wrappedList = new MultiListWrapper<TheModel>(5, ids, names, bools);
then request an element like so:
var bob = wrappedList[2];
it's enumerable as well:
foreach (var item in wrappedList)
{
Console.WriteLine("{0}\t{1}\t{2}", item.Id, item.Name, item.YesOrNo);
}
Checks are being done in the indexer to ensure the types on the array elements match the property type on the Model. Ordering is going to be important with the order of the array parameters on the constructor needing to match the order the Model elements are declared.

How to update an object in a List<> in C#

I have a List<> of custom objects.
I need to find an object in this list by some property which is unique and update another property of this object.
What is the quickest way to do it?
Using Linq to find the object you can do:
var obj = myList.FirstOrDefault(x => x.MyProperty == myValue);
if (obj != null) obj.OtherProperty = newValue;
But in this case you might want to save the List into a Dictionary and use this instead:
// ... define after getting the List/Enumerable/whatever
var dict = myList.ToDictionary(x => x.MyProperty);
// ... somewhere in code
MyObject found;
if (dict.TryGetValue(myValue, out found)) found.OtherProperty = newValue;
Just to add to CKoenig's response. His answer will work as long as the class you're dealing with is a reference type (like a class). If the custom object were a struct, this is a value type, and the results of .FirstOrDefault will give you a local copy of that, which will mean it won't persist back to the collection, as this example shows:
struct MyStruct
{
public int TheValue { get; set; }
}
Test code:
List<MyStruct> coll = new List<MyStruct> {
new MyStruct {TheValue = 10},
new MyStruct {TheValue = 1},
new MyStruct {TheValue = 145},
};
var found = coll.FirstOrDefault(c => c.TheValue == 1);
found.TheValue = 12;
foreach (var myStruct in coll)
{
Console.WriteLine(myStruct.TheValue);
}
Console.ReadLine();
The output is 10,1,145
Change the struct to a class and the output is 10,12,145
HTH
or without linq
foreach(MyObject obj in myList)
{
if(obj.prop == someValue)
{
obj.otherProp = newValue;
break;
}
}
Can also try.
_lstProductDetail.Where(S => S.ProductID == "")
.Select(S => { S.ProductPcs = "Update Value" ; return S; }).ToList();
You can do somthing like :
if (product != null) {
var products = Repository.Products;
var indexOf = products.IndexOf(products.Find(p => p.Id == product.Id));
Repository.Products[indexOf] = product;
// or
Repository.Products[indexOf].prop = product.prop;
}
var itemIndex = listObject.FindIndex(x => x == SomeSpecialCondition());
var item = listObject.ElementAt(itemIndex);
item.SomePropYouWantToChange = "yourNewValue";
This was a new discovery today - after having learned the class/struct reference lesson!
You can use Linq and "Single" if you know the item will be found, because Single returns a variable...
myList.Single(x => x.MyProperty == myValue).OtherProperty = newValue;
I found a way of doing it in one Line of code unsing LINQ:
yourList.Where(yourObject => yourObject.property == "yourSearchProperty").Select(yourObject => { yourObject.secondProperty = "yourNewProperty"; return yourObject; }).ToList();
var index = yourList.FindIndex(x => x.yourProperty == externalProperty);
if (index > -1)
{
yourList[index] = yourNewObject;
}
yourlist now has the updated object inside of it.
It is also worth mentioning that what Matt Roberts said applies to structs inside classes.
So even when you have a class (which will pass by reference, theoretically), a property which is a list of structs inside it will pass by value when you try to find it and change a value inside a given struct (on the list).
For instance (using the same code as Matt proposed):
ParentClass instance = new ParentClass();
var found = instance.ListofStruct.FirstOrDefault(c => c.TheValue == 1);
found.TheValue = 12;
foreach (var myStruct in instance.ListofStruct)
{
Console.WriteLine(myStruct.TheValue);
}
Console.ReadLine();
public class ParentClass
{
public List<MyStruct> ListofStruct { get; set; }
public struct MyStruct
{
public int TheValue { get; set; }
}
public ParentClass()
{
ListofStruct = new List<MyStruct>()
{
new MyStruct {TheValue = 10},
new MyStruct {TheValue = 1},
new MyStruct {TheValue = 145}
};
}
}
Will output: 10, 1, 145
Whereas changing the struct (inside the ParentClass) to a class, like this:
public class ParentClass
{
public List<MySubClass> ListofClass { get; set; }
public class MySubClass
{
public int TheValue { get; set; }
}
public ParentClass()
{
ListofClass = new List<MySubClass>()
{
new MySubClass {TheValue = 10},
new MySubClass {TheValue = 1},
new MySubClass {TheValue = 145}
};
}
}
Will output: 10, 12, 145
//Find whether the element present in the existing list
if (myList.Any(x => x.key == "apple"))
{
//Get that Item
var item = myList.FirstOrDefault(x => x.key == ol."apple");
//update that item
item.Qty = "your new value";
}

Calculate average in LINQ C# with string representation of property name

I need to calculate a whole bunch of averages on an List of Surveys. The surveys have lots of properties that are int and double valued. I am creating a business object to handle all the calculations (there are like 100) and I'd rather not code 100 different methods for finding the average for a particular property.
I'd like to be able to have the UI pass a string (representing the property) and have the the business object return an average for that property.
So, like...
int AverageHeightInInches = MyObject.GetIntAverage("HeightInInches");
.
.
.
Then have linq code to calculate the result.
Thanks!
I have created this little example, it uses the System.Linq.Expression namespace to create a function that can calculate averages based on the property name. The function can be cached for later use, reflection is only used to create the function, not each time the function is executed.
EDIT: I removed the existing reflection example and updated the current example to show the ability to walk a list of properties.
static class Program
{
static void Main()
{
var people = new List<Person>();
for (var i = 0; i < 1000000; i++)
{
var person = new Person { Age = i };
person.Details.Height = i;
person.Details.Name = i.ToString();
people.Add(person);
}
var averageAgeFunction = CreateIntegerAverageFunction<Person>("Age");
var averageHeightFunction = CreateIntegerAverageFunction<Person>("Details.Height");
var averageNameLengthFunction = CreateIntegerAverageFunction<Person>("Details.Name.Length");
Console.WriteLine(averageAgeFunction(people));
Console.WriteLine(averageHeightFunction(people));
Console.WriteLine(averageNameLengthFunction(people));
}
public static Func<IEnumerable<T>, double> CreateIntegerAverageFunction<T>(string property)
{
var type = typeof(T);
var properties = property.Split('.'); // Split the properties
ParameterExpression parameterExpression = Expression.Parameter(typeof(T));
Expression expression = parameterExpression;
// Iterrate over the properties creating an expression that will get the property value
for (int i = 0; i < properties.Length; i++)
{
var propertyInfo = type.GetProperty(properties[i]);
expression = Expression.Property(expression, propertyInfo); // Use the result from the previous expression as the instance to get the next property from
type = propertyInfo.PropertyType;
}
// Ensure that the last property in the sequence is an integer
if (type.Equals(typeof(int)))
{
var func = Expression.Lambda<Func<T, int>>(expression, parameterExpression).Compile();
return c => c.Average(func);
}
throw new Exception();
}
}
public class Person
{
private readonly Detials _details = new Detials();
public int Age { get; set; }
public Detials Details { get { return _details; } }
}
public class Detials
{
public int Height { get; set; }
public string Name { get; set; }
}
Here is an example to do that.
class Survey
{
public int P1 { get; set; }
}
class MyObject
{
readonly List<Survey> _listofSurveys = new List<Survey> { new Survey { P1 = 10 }, new Survey { P1 = 20 } };
public int GetIntAverage(string propertyName)
{
var type = typeof(Survey);
var property = type.GetProperty(propertyName);
return (int)_listofSurveys.Select(x => (int) property.GetValue(x,null)).Average();
}
}
static void Main(string[] args)
{
var myObject = new MyObject();
Console.WriteLine(myObject.GetIntAverage("P1"));
Console.ReadKey();
}
if you are using linq2sql i would suggest DynamicLinq
you could then just do
datacontext.Surveys.Average<double>("propertyName");
the dynamic linq project provides the string overloads to IQueryable.
You can do this without reflection (both int and double are supported):
public static double Average(this IEnumerable<Survey> surveys, Func<Survey, int> selector)
{
return surveys.Average(selector);
}
public static double Average(this IEnumerable<Survey> surveys, Func<Survey, double> selector)
{
return surveys.Average(selector);
}
Usage:
var average1 = surveys.Average(survey => survey.Property1);
var average2 = surveys.Average(survey => survey.Property2);

Categories