Loop through class properties and get distinct values - c#

How can I do this without going through each class property separately? I have a class with 54 properties that I want to go through and retrieve values from CosmosDB and then use those Distinct values from each property to create a dropdownfo in the view.
I can do them all separately - this code works fine. But to do that for all the properties... there has to be an easier way.
I have to do this with bankCountryCode, Currency, etc. All properties of a class FieldMasterInfo.
IEnumerable<string> fieldId = allItems.Select(m => m.FieldId).Distinct();
var fieldSL = new List<SelectListItem>();
foreach (var element in fieldId)
{
// adding null check here until cosmos db with null tenant id's for tax are removed.
if (element != null)
{
fieldSL.Add(new SelectListItem
{
Value = element.ToString(),
Text = element.ToString()
});
}
}
fieldSL = fieldSL.OrderBy(x => x.Text).ToList();
ViewBag.fieldIdList = fieldSL;
Update
This worked perfectly. It loops through a class and get's the distinct values from each property and puts it into a Dictionary>
FieldMasterInfo fmi = new FieldMasterInfo();
PropertyInfo[] properties = fmi.GetType().GetProperties();
Dictionary<string, IEnumerable<object>> sl = new Dictionary<string, IEnumerable<object>>();
foreach (PropertyInfo property in properties)
{
sl[property.Name] = allItems.Select(m => m.GetType().GetProperty(property.Name).GetValue(m, null)).Distinct();
}

Pass in your fields as text in an array, then use reflection to dynamically read the property.
var fieldNames = new string[] {"bankCountryCode", "Currency" };
foreach(var fieldName in fieldNames)
{
IEnumerable<string> fieldId = allItems.Select(m =>
m.GetType().GetProperty(fieldName).GetValue(m, null)).Distinct();
var fieldSL = new List<SelectListItem>();
foreach (var element in fieldId)
{
// adding null check here until cosmos db with null tenant id's for tax are removed.
if (element != null)
{
fieldSL.Add(new SelectListItem
{
Value = element.ToString(),
Text = element.ToString()
});
}
}
fieldSL = fieldSL.OrderBy(x => x.Text).ToList();
ViewBag.fieldIdList = fieldSL;
}

I confess I did not understand your question clearly. But is this what you need? (I could not test, I wrote right here.)
IEnumerable<string> fieldId = allItems.Select(m => m.FieldId).Distinct();
var fieldSL = fieldId.Where(f => !string.IsNullOrEmpty(f)).Select(x => new SelectListItem { Text = f, Value = f })).OrderBy(i => i.Text).ToList();
ViewBag.fieldIdList = fieldSL;
Either way, better clarify the situation so others can help you. If you need smaller code or another logic or business solution ...

Related

Condense similiar methods into one mega cool method

I'm trying to remove duplicated code and run into an issue here:
I've got five very similar entities (different asset types, e.g. Bonds, Stocks). The methods I'm trying to condense return some statistics about these assets. The statistics are obtained with the help of Linq, the queries are almost identical.
Before, I had five separate methods in my controller (e.g. BondStatistics, StockStatistics). One of these would look like this (db is my database context which has each asset type defined):
public JsonResult BondStatistics()
{
var items = db.Bonds.ToList();
var result = new[]
{
new
{
key = "Bonds",
values = items.Select(i =>
new {
x = i.priceChangeOneDayInEuro,
y = i.priceChangeTotalInEuro,
size = i.TotalValueInEuro,
toolTip = i.Description
}
)
},
};
return Json(result, JsonRequestBehavior.AllowGet);
}
I googled that one way to rewrite these into just one method could be using reflection. However, I thought I could use a dirty shortcut, something like this:
public JsonResult Scatter(string asset)
{
if (asset == "Stocks") { var items = db.Stocks.ToList(); };
if (asset == "Bonds") { var items = db.Bonds.ToList(); };
if (asset == "Futures") { var items = db.Futures.ToList(); };
if (asset == "Options") { var items = db.Options.ToList(); };
if (asset == "Funds") { var items = db.Funds.ToList(); }
var result = new[]
{
new
{
key = asset,
values = items.Select(i =>
new {
x = i.priceChangeOneDayInEuro,
y = i.priceChangeTotalInEuro,
size = i.TotalValueInEuro,
toolTip = i.Description
}
)
},
};
return Json(result, JsonRequestBehavior.AllowGet);
}
This leads to the problem that the type of "items" is not known in the Linq query at design time.
What would be a good way to overcome this problem? Use some totally other pattern, do use reflection or is there an easy fix?
EDIT
As suggested, I created an Interface and let the BaseAsset-class implement it. Then, changing the condensed method to
List<IScatter> items = new List<IScatter>();
if (asset == "Stocks") { items = db.Stocks.ToList<IScatter>(); };
if (asset == "Bonds") { items = db.Bonds.ToList<IScatter>(); };
if (asset == "Futures") { items = db.Futures.ToList<IScatter>(); };
if (asset == "Options") { items = db.Options.ToList<IScatter>(); };
if (asset == "Funds") { items = db.Funds.ToList<IScatter>(); }
works, at design time at last. Thank you very much!
You are putting everything into var, but what exactly is the type of the items you are processing?
If it would be List<Stock> for db.Stocks.ToList(), List<Bond> for db.Bonds.ToList() you can simply define an interface (e.g. IHasPriceInformation) which has the fields you are using in the LINQ query. Then, Let Stock, Bond and others implement this interface (or provide an abstract base implementation of them) and simply run your LINQ Query on a List<IHasPriceInformation>.

LINQ - Assign a Value to Anonymous Type's Read Only Property

I would like to create an anonymous type from linq. Then change the value of a single property(status) manually and give the list to a repeater as data source. But doesn't let me do that as theay are read-only. Any suggestion?
var list = from c in db.Mesai
join s in db.MesaiTip on c.mesaiTipID equals s.ID
where c.iseAlimID == iseAlimID
select new
{
tarih = c.mesaiTarih,
mesaiTip = s.ad,
mesaiBaslangic = c.mesaiBaslangic,
mesaiBitis = c.mesaiBitis,
sure = c.sure,
condition = c.onaylandiMi,
status = c.status
};
foreach (var item in list)
{
if (item.condition==null)
{
item.status == "Not Confirmed";
}
}
rpCalisanMesai.DataSource = list.ToList();
rpCalisanMesai.DataBind();
Instead of trying to change the value after creating the list, just set the right value while creating the list.
var list = from c in db.Mesai
join s in db.MesaiTip on c.mesaiTipID equals s.ID
where c.iseAlimID == iseAlimID
select new
{
tarih = c.mesaiTarih,
mesaiTip = s.ad,
mesaiBaslangic = c.mesaiBaslangic,
mesaiBitis = c.mesaiBitis,
sure = c.sure,
condition = c.onaylandiMi,
status = c.onaylandiMi != null ? c.status : "Not Confirmed"
};
Also, if you could change the property, your problem would be executing the query twice: first in the foreach-loop, and then again by calling list.ToList() (which would create new instances of the anonymous type).
You cannot, anonymous type's properties are read-only.
You need to set it during object creation. See #Dominic answer for code sample.
You can. For instance:
var data = (from a in db.Mesai select new { ... status = new List<string>() .. }).ToList();
Next, compute your status:
foreach (var item in data) {
item.status.Add("My computed status");
}
And then on rendering:
foreach (var item data) {
Response.Write(item.status[0]);
}
EDIT: The list can even be intialized as per your requirement:
var data = (from a in db.Mesai select new { ... status = new List<string>(new
string[] { c.status }) .. }).ToList();
foreach (var item in data) {
item.status[0] = "My computed status";
}
EDIT2: Seems like you must initialize the list, preferably with e.g. c.rowid.ToString(), otherwise the optimizer assigns the same new List() to all items, thinking that this might be some game or something.

Update a List<T> witha SubList<T> performance (LINQ or Not)

I have a list which can have ~200'000 items. I need to update a fixed number of fields for, for instance 150 item, using a sub-list. This is the code I am using right now:
listItem: 200'000 item List - subListItem: 150 item List (updated)
listItem.Select(item =>
{
if (subListItem.Exists(x => x.ID == item.ID))
{
var currentItem = subListItem.Single(x => x.ID == item.ID);
item.FIELD_1 = currentItem.FIELD_1;
item.FIELD_2 = currentItem.FIELD_2;
item.FIELD_3 = currentItem.FIELD_3;
item.FIELD_4 = currentItem.FIELD_4;
}
return item;
}).ToList();
This is working good, but performance are really poor. Have you any advice ?
UPDATED SOLUTION:
dictItem = listItem.ToDictionary(x => x.ID);
foreach (Item updatedItem in subListItem)
{
Item originalItem = dictItem[updatedItem.ID];
originalItem.FIELD_1 = updatedItem.FIELD_1;
originalItem.FIELD_2 = updatedItem.FIELD_2;
originalItem.FIELD_3 = updatedItem.FIELD_3;
originalItem.FIELD_4 = updatedItem.FIELD_4;
dictItem[updatedItem.ID] = originalItem;
}
Make use of a dictionary to make the item lookup much quicker, i.e.
var subListItemsById = subListItems.ToDictionary(x => x.ID);
foreach(var item in listItem)
{
SubListItem subListItem;
if(subListItemsById.TryGetValue(item.ID, out subListItem))
{
item.FIELD_1 = subListItem.FIELD_1;
item.FIELD_2 = subListItem.FIELD_2;
item.FIELD_3 = subListItem.FIELD_3;
item.FIELD_4 = subListItem.FIELD_4;
}
}
Your original listItem will contain the modified objects. This will only work if the items contained in listItem are reference types. If they are mutable structs you will need to do similar to your example:
var subListItemsById = subListItems.ToDictionary(x => x.ID);
var modifiedItems = listItem.Select(item =>
{
SubListItem subListItem;
if(subListItemsById.TryGetValue(item.ID, out subListItem))
{
item.FIELD_1 = subListItem.FIELD_1;
item.FIELD_2 = subListItem.FIELD_2;
item.FIELD_3 = subListItem.FIELD_3;
item.FIELD_4 = subListItem.FIELD_4;
}
return item;
}).ToList();
If you need better performance, you need to use a container that is optimized for lookups. For example a Dictionary using your ID as key.
You can speed it up a lot by creating a dictionary as stated. However you can also keep the original list if you don't have to create a copy, reducing memory footprint.
var subItemLookup = subItems.ToDictionary(i => i.Id);
foreach (var item in items)
{
SubItem subItem;
if (subItemLookup.TryGetValue(item.Id, out subItem))
{
item.Field1 = subItem.Field1;
//etc.
}
}

Linq group by using reflection

I have data table "Car" which have 3 cols (owner, carType, colour). My question is how can i make the grouping portion more dynamic by using reflection. my idea is add the grouping col in to array, then use the reflection on the query grouping part. however i was struck at the reflection..
var gcols = new string[] { "owner", "carType" };
var reseult = dt.AsEnumerable()
.GroupBy(x => new
{
carType = x.Field<string>("carType"),
colour = x.Field<string>("colour")
})
.Select(x => new
{
CarType = x.Key.carType,
Colour = x.Key.colour,
count = x.Count()
})
.OrderBy(x => x.CarType).ToList();
If you added this extension method to object:
public static T Field<T>(this object source, string FieldName)
{
var type = source.GetType();
var field = type.GetField(FieldName);
return (T)field.GetValue(source);
}
You'd be able to use the syntax you've posted in your code.
I've not added any safety checking here so it'll need cleaning up, but it'll get you going. Ideally you'd want to check that the type of field is the same as T and a few other checks.

Replace all values in IQueryable with string based on results using LINQ

I have an IQueryable I selected from a database that contains a field called "NewValue". This field will either contain a 0, 1, or 2. I want to replace the value with a string based of which number it is.
i.e 0 = "Active". I know IQueryables aren't used to change values mostly just for querying but I need to use it in the next line to add to the ViewModel
var AssessmentList = assessment
.Select(p => new LogManagementViewModel
{
newValue = p.NewValue,
});
How can I change all the values in assement.NewValue to a string?
var dictionary = new Dictionary<int, string>
{
{ 0, "Active" },
{ 1, "Inactive" },
// etc
};
var AssessmentList = assessment
.Select(p => new LogManagementViewModel
{
newValue = dictionary[p.NewValue],
});
This would work for mapping between other types as well.
var values = new [] {"StrValue1", "StrValue2", "StrValue"};
var AssessmentList = assessment
.AsEnumerable()
.Select(p => new LogManagementViewModel
{
newValue = values[p.NewValue],
});
Easiest and cleanest would be with a mapping method:
string MapToString(int value)
{
//..
}
var AssessmentList = assessment
.Select(p => new LogManagementViewModel
{
NewValue = MapToString(p.NewValue),
});
One way:
Add a new computed property onto your class (assuming you have a partial class outside of any generated code). Then that computed class can do the translation however it needs to. Your view model can reference this computed value.
I would make a constructor overload in your LogManagementViewModel that does that work for you, something like this:
public LogManagementViewModel(int NewValue)
{
switch(NewValue)
{
case 0:
this.NewValue = "Active";
break;
// etc...
}
}
IMO, this places the logic in it's proper place, let the your view model translate it as it should be, it's not the responsibility of your LINQ/Database queries.

Categories