Use LINQ to transform single object into IEnumerable via Properties/reflection? - c#

I have a simple object like such:
public class Foo {
public int One { get; set; }
public int Two { get; set; }
....
public int Eleven { get; set; }
}
Given an IEnumerable, what I want is a LINQ method to transform as such:
myFooEnumerable.Select(n => transformMagicGoesHere);
Where my return object looks like this:
public class Bar {
public string DurationDescription {get;set;} //Value would be "One" or "Two" or ...
public int Value {get;set;} //Holds value in the property One or Two or ...
}
So for every item N in myFooEnumerable in the example above I'd get 11(N) items in my resultant select statement.

This should do it:
var bars = myFooEnumerable.SelectMany(
x => x.GetType().GetProperties().Select(p => new Bar {
DurationDescription = p.Name,
Value = (int)p.GetValue(x)
}));
Not a great thing to be doing in the first place, IMO, but it will at least work.

Related

How to add items to existing list of objects?

I have three classes:
public class M2ArticleMain
{
public int Id { get; set; }
public List<M2ArticleAttributeWeb> Attribut_Web { get; set; }
}
public class M2ArticleAttributeWeb
{
public int Web_Id { get; set; }
public M2ArticleTmpMainSkus Variants { get; set; }
}
public class M2ArticleTmpMainSkus
{
public DateTime TimeAdded { get; set; }
public List<string> Skus { get; set; }
}
And I have two Lists in my code like this:
List<M2ArticleMain> data = new List<M2ArticleMain>();
List<M2ArticleAttributeWeb> attb = new List<M2ArticleAttributeWeb>();
In some part of my code firstly I (from foreach loop) add data to attb list where I add only only some data (because I don't have all data at this point), like this:
...
attb.Add(new M2ArticleAttributeWeb
{
Web_id = item.Id, //(item is from foreach loop)
Variants = null //this is **importat**, I left null for later to add it
});
Next, after I fill attb, I add all this to data list:
...
data.Add(new M2ArticleMain
{
Id = item.Id_Pk, //this is also from foreach loop,
Attribut_Web = attb //now in this part I have only data for Web_id and not Variants
}
Now my question is How to Add items later to data list to object Variants?
Something like this:
data.AddRange( "how to point to Variants" = some data);
The M2ArticleAttributeWeb type holding your Variants property is the member of a collection. That is, there are potentially many of them. You can reference an individual Variants property like this:
data[0].Attribut_Web[0].Variants
But you need to know which items you want to add map to which data and Attribut_Web indexes/objects in order to assign them properly. That probably means another loop, or even a nested loop. That is, you can see all of your Variants properties in a loop like this:
foreach(var main in data)
{
foreach(var attrw in main)
{
var v = attrw.Variants;
// do something with v
Console.WriteLine(v);
// **OR**
attrw.Variants = // assign some object
}
}
It's also much better practice to create your collection properties with the object, and then give them private set attributes:
public class M2ArticleMain
{
public int Id { get; set; }
public List<M2ArticleAttributeWeb> Attribut_Web { get; private set; } = new List<M2ArticleAttributeWeb>();
}
public class M2ArticleAttributeWeb
{
public int Web_Id { get; set; }
public M2ArticleTmpMainSkus Variants { get; set; }
}
public class M2ArticleTmpMainSkus
{
public DateTime TimeAdded { get; set; }
public List<string> Skus { get; private set; } = new List<string>();
}
Now instead of assigning Attribut_Web = attb, you would need to .Add() to the existing List.

an array list with n rows and four columns in c#

I need a clear example that shows me how to define a list that has n rows and 4 columns and how to use it. I need a list to save my data like the below image. as you see this could be a dictionary.
You need to create a class with all the above properties
public class Sample
{
public string vocabulary { get; set; }
public string meaning { get; set; }
public int number { get; set; }
public int group { get; set; }
}
and then you can create a List of type Sample,
List<Sample> yourList = new List<Sample>();
You can add items to the list as below
yourList.Add(new Sample { vocabulary = "massive", meaning = "very big", number = 5, group = 15 });
You can access them later like this, if you want the first element,
var result = yourList[0];
this is the easiest and best way of doing it. You need to create a new class and then create new instances of the class and then add it to the list and then use LINQ to get the data out
void Main()
{
var list = new List<myClass>()
list.Add(new myClass() {
Vocabluary = "Vocabluary ",
Meaning = "meaning",
Number = 1,
Group = 2})
}
public class myClass
{
public string Vocabluary { get; set; }
public string Meaning { get; set; }
public int Number { get; set; }
public int Group { get; set; }
}
yes... as Sajeetharan mentioned, with a custom class you can create an any dimensions List. but i don't think you need to think about dimension in C#... it is a bit more high level than that.
just simply create a class and put everything you need in it...
public class CustomClass{
public string d1;
public int d2;
public string d3;
public string d4;
...
//you can easily create a N dimension class
}
to access it and apply it
public void Main(){
List<CustomClass> list = new List<CustomClass>();
CustomClass cc = new CustomClass();
cc.d1 = "v1";
cc.d2 = 0; //v2
list.Add(cc);
//to access it
foreach(CustomClass tmpClass in list)
{
string d1Value = tmpClass.d1;
int d2Value = tmpClass.d2;
}
}

Converting from on type of list to another type

I have two classes - Record and RecordModified
public class Record
{
public int RecordID { get; set; }
public int FacilityID { get; set; }
public int NewAID { get; set; }
public string OldID { get; set; }
public string Data { get; set; }
public int SyncStatusID { get; set; }
public int RecordTypeID { get; set; }//RecordTypeID is integer here.
}
The second class
public class RecordModified
{
public int RecordID { get; set; }
public int FacilityID { get; set; }
public int NewAID { get; set; }
public string OldID { get; set; }
public string Data { get; set; }
public int SyncStatusID { get; set; }
public string RecordTypeText { get; set; }//RecordTypeText is string here.
}
I have a list of Record with at least 100 Record objects, now I have to convert the List<Record> into List<RecordModified>. The RecordTypeID property of the Record class has to be converted to the property RecordTypeText of RecordModified using enums which are in a different class.
Code snippet on how I'm trying to convert:
foreach(Record r in List<Record>)
{
switch(r.RecordTypeID)
{
case (int)MyEnum.One:
listofRecordModified.Add(new RecordModified{RecordTypeID=r.RecordTypeID,...,**RecordTypeText=(MyEnum.One).ToString()})** // Notice this
break;
...........//75 more cases.
}
This solution works fine, but the problem is lot of codes and I don't think its efficient. There must be some better way to do that. Please suggest.
I think you can use ConvertAll Method along with this
List<Record> t = new List<Record>();
var result = t.ConvertAll(x => new RecordModified()
{
RecordTypeText = ((MyEnum)x.RecordTypeID).ToString()
});
If your sole problem is the conversion of the index of an enum to its text, you could use GetNames and use the index of the to get the name of the enum value used.
string text = Enum.GetNames(typeof(MyEnum))[r.RecordTypeID];
This way, you don't need the switch statement, and you can revert to one line only.
You can just use the You can do using the (MyEnum)x.RecordTypeID to cast the integer value to matching enum value. and then use that .ToString() to get string value.Linq Lambda expressions as below,
var result = RecordList.Select(x=>new RecordModified{
RecordTypeID=x.RecordTypeID,
...,
RecordTypeText=((MyEnum)x.RecordTypeID).ToString()
});
You can also use the ConvertAll as,
var result = RecordList.ConvertAll(x => new RecordModified()
{
RecordTypeText = ((MyEnum)x.RecordTypeID).ToString()
});
Select is a LINQ extension method and works on all IEnumerable<> objects whereas ConvertAll is implemented only by List<>. The ConvertAll method exists since .NET 2.0 whereas LINQ was introduced with 3.5.
You should favor Select over ConvertAll as it works for any kind of list, but they do the same basically.
I believe you have each switch statement for each MyEnum value?
That is a lot of repeated code, that's true.
Why don't you just convert int to text value directly?
You can do it like this for more readability
foreach(Record r in List<Record>)
{
MyEnum myEnum = (MyEnum)r.RecordTypeID;
string stringValue = myEnum .ToString();
listofRecordModified.Add(new RecordModified{RecordTypeID=r.RecordTypeID, ...,**RecordTypeText=stringValue })** // Notice this
}

ravendb index across multiple nested properties

I am asking how to create an index based upon two different nested properties on an document. I am executing these queries through C#.
public class LocationCode
{
public string Code {get;set;}
public string SeqId {get;set;}
}
public class ColorCode
{
public string Code {get;set;}
public string SeqId {get;set;}
}
public class TestDocument
{
public int Id {get;set;}
public List<LocationCode> Locations { get; set; }
public List<ColorCode> Colors { get; set; }
}
I have experimented with various AbstractIndexCreationTask, Map, and Map+Reduce, but to no avail.
I would like to be able to do a query such as:
Get all documents where any Locations.Code property is "USA", AND/OR Colors.Code="RED", or on the SeqId property. I dont know whether this would mean I need multiple indexes. Normally I would either be comparing the Code property on both nested classes, or the Seq, but never mixed.
Please could someone point me in the right direction.
Many thanks
Phil
Create your index like this:
public class TestIndex : AbstractIndexCreationTask<TestDocument, TestIndex.IndexEntry>
{
public class IndexEntry
{
public IList<string> LocationCodes { get; set; }
public IList<string> ColorCodes { get; set; }
}
public TestIndex()
{
Map = testDocs =>
from testDoc in testDocs
select new
{
LocationCodes = testDoc.Locations.Select(x=> x.Code),
ColorCodes = testDoc.Colors.Select(x=> x.Code)
};
}
}
Then query it like this:
var q = session.Query<TestIndex.IndexEntry, TestIndex>()
.Where(x => x.LocationCodes.Any(y => y == "USA") &&
x.ColorCodes.Any(y => y == "RED"))
.OfType<TestDocument>();
Full unit test here.

linq - retrieving data dependent on the object type

I have a structure like this
public class ItemBase
{
public int ItemId { get; set; }
public DateTime Created { get; set; }
public ItemType Type { get; set; }
}
public class RockItem : ItemBase { }
public class PlantItem : ItemBase
{
public bool IsDeadly { get; set; }
}
public class AnimalItemBase : ItemBase
{
public int NumberOfLegs { get; set; }
public bool IsDeadly { get; set; }
}
public class DogItem : AnimalItemBase { }
public class CatItem : AnimalItemBase { }
There is a type flag in the database and I use Fluent to split out on type and return an IEnumerable<ItemBase>
This works for most of what I want, but now I am in a situation where I need to meld the items together. For instance, I want the ItemId, IsDeadly, and the NumberOfLegs returned in an anonymous object. The results have to be sorted on the Created field in one list. Is there an easy way to do this with linq? Ideally, I would not have to split these out, merge the results, and then sort.
The example you give could be solved using OfType:
IEnumerable<ItemBase> items = ...
var results = items.OfType<AnimalItemBase>()
.OrderBy(x => x.Created).ToList();
If you have to support combinations of properties that cross classes i.e all items that have the IsDeadly property, you could use a combination of reflection to check the properties you want to use and dynamic to enable the duck typing you need, since technically these are different IsDeadly properties, you just know they should be treated the same in your scenario.
Doing that you can then assign the properties in your anonymous type dynamically. I.e. the following example returns results for all of your types that have the IsDeadly property:
var results = items.OrderBy(x => x.Created)
.Where(x => x.GetType().GetProperty("IsDeadly") !=null)
.Select( x =>
{
dynamic o = x;
return new { IsDeadly = o.IsDeadly, Created = o.Created };
})
.ToList();
Also as #Henk Holterman pointed out, it only makes sense to return an enumeration of anonymous types where each property of the returned type makes sense / is defined for all the items in the enumeration.

Categories