Remove duplication's from my DropDownList - c#

In my controller i am returning list of my object with specific property:
public ActionResult Index()
{
List<MyObject> list = db.MyObjects.Where(x => x.family == "Web").ToList();
ViewBag.Files = new SelectList(list, "Id", "protocol");
return View();
}
This is my object:
public class MyObject
{
public int id { get; set; }
public string fileName { get; set; }
public string browser { get; set; }
public string protocol { get; set; }
public string family { get; set; }
}
Index.cshtml:
#Html.DropDownList("File", new SelectList(ViewBag.Files, "Id", "protocol_site"), "Select webmail site", new { style = "vertical-align:middle;" })
And i try to made 2 changes with no succeed:
Remove all the duplication protocol from my DropDownListfor exapmle if i have 10 objects : 9 is doc protocol and 1 pdf i wand to see in my DropDownList only 2 items: DOC and PDF and not all the 10 items.
Sort this DropDownList in alphabet order

As #Dreamcatcher mentioned in his answer, you should pass already prepared collection to SelectList constructor and use linq for these tasks. For Distinct linq method you will need to create custom comparer, which will compare objects by protocol field:
public sealed class MyObjectByProtocolComparer: IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
return x.protocol.Equals(y.protocol);
}
public int GetHashCode(MyObject obj)
{
return obj.protocol.GetHashCode();
}
}
This is rather simple implementation, you might need to check for null values. After that use it in your linq query:
var list = db.MyObjects.Where(x => x.family == "Web").ToArray();
list = list.Distinct(new MyObjectByProtocolComparer())
.OrderBy(x => x.fileName)
.ToArray();

You should add second line in your code. However i am not sure correct spelling, i did not use VS. Also if Disctinct does not work correctly, you should write Comparer.
List<MyObject> list = db.MyObjects.Where(x => x.family == "Web").ToList();
list= list.OrderBy(x => x.fileName).Distinct().ToList();

Follow the guidelines in this page http://msdn.microsoft.com/en-us/library/ms173147(v=vs.80).aspx so that you can call linq Distinct

Related

getting individual fields from a linq query which is run from a function

I have a function that return a linq result :
private IEnumerable<object> prepareData()
{
var data = from res in Globals.ds.Tables[0].AsEnumerable()
.GroupBy(x => new
{
art = x.Field<string>("artiste"),
alb = x.Field<string>("album"),
})
.Select(p => new
{
album = p.Key.alb,
artiste = p.Key.art,
count_lab = p.Count(),
lab = p.Select(x => x.Field<string>("label")).First(),
filp = p.Select(x => x.Field<string>("file_path")).First()
})
.OrderBy(x => x.lab)
select res;
return data;
}
The query works well as designed, i can do data = PrepareData(); and get the right results.
My issue is when i want to do a .where on the data.
if i do :
var album = data.Where(x => x.
Then i dont have any option to select a single field (it's the same if i want to do a .Select()).
I tried data.AsEnumerable() before but to no success.
I'm thinking the IEnumerable<object> prepareData() is the culprit, but i have no idea how to fix this (if ever it's the case).
I need help
Thanks in advance
If you want to select a single field you can use: First() or FirstOrDefault(). Difference between this two is:
First() will throw an exception if an element is not found.
FirstOrDefault() will return null if element is not found.
Also if you want to fix the problem with IEnumerable<object> you need to create an DTO class where you can map all items from select.
Something like this:
public class DTOClass
{
public string album { get; set; }
public string artiste { get; set; }
public string count_lab { get; set; }
public string lab { get; set; }
public string filp { get; set; }
}
And then in select you can simply do:
...
Select(p => new DTOClass {
// map the values for DTO class here
}

Union two list by property

I would like to merge two list without duplicates. It should distinct only by one property.
I have a class:
public class Test
{
public int Id { get; set; }
public string Prop { get; set; }
public string Type { get; set; }
}
I have two lists which I would like to merge without duplicates by Type. So, firstly I want to take everything from list 1 and then from list 2 when Type doesn't exist in list 1.
I've tried union.
You've to use IEqualityComparer. See at : https://msdn.microsoft.com/en-us/library/ms132151%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
class Compare : IEqualityComparer<Test>
{
public bool Equals(Test x, Test y)
{
return x.Id == y.Id;
}
public int GetHashCode(Test codeh)
{
return codeh.Id.GetHashCode();
}
}
Then use
var union = list1.Union(list2).Distinct(new Compare()).ToList();
Using .NET 6 or higher, you can use the UnionBy() method which will merge 2 lists excluding duplicates by a property :
var mergedLists = firstList.UnionBy(secondList, u => u.property).ToList();
For more informations : Microsoft doc

Trying to get a list of properties dynamically where count > 0

I have a model like so
public class UserModel
{
List<UserModel> users
}
public class UserModel
{
public List<UserSomeObj> userSomeObj { get; set; }
public List<UserSomeOtherObj> userSomeOtherObj { get; set; }
}
public class UserSomeObj
{
public int someIntProperty { get; set; }
public string someStringProperty { get; set; }
}
public class UserSomeOtherObj
{
public int someIntProperty { get; set; }
public string someStringProperty { get; set; }
}
Each UserModel class List is comprised of several other class Lists.
I am referencing them dynamically like so by looping over a list of targeted properties.
to get a list of properties matching the 'prop' variable;
var props = MethodToGetTargetedProperties();
// props example content would be a list of strings like so "UserSomeObj", "UserSomeOtherObj"
foreach (var prop in props)
{
var results = users.Select(x => x.GetPropertyValue(prop)).ToList();
//results contain lists of prop where count == 0 and i dont want them
}
what I am trying to do is reduce the results where count of the lists targeted is greater than 0 .... problem is that I can't find the correct order/syntax to get it to work.
Thanks
As List<T> implements the non-generic ICollection interface, you can cast to that:
var results = users.Select(x => x.GetPropertyValue(prop))
.Cast<ICollection>()
.Where(list => list.Count > 0)
.ToList();
You could do the cast within the Where if you want, although I prefer the above:
var results = users.Select(x => x.GetPropertyValue(prop))
.Where(list => ((ICollection) list).Count > 0)
.ToList();
Thanks ..... in reviewing your reply I was able to do the following
var results = users.Select(x => x.GetPropertyValue(prop))
.Cast<IEnumerable<object>>().Where(y => y.Count() > 0).ToList();
Then after looking at your answer more and trying to understand all its facets, I wondered if I could do it all in one line. The two List classes in UserModel have 2 common properties (idSomething and idSomeOtherThing) and since I will be combining them and doing a Distinct, i thought, hmm, one liner might be possible.

Group By is not aggregating

I am aggregating data that I retrieve from multiple identical web services. The same row count and data points are returned with only a variance in the Value. The GroupBy clause I am using is not condensing any of the rows. I have the same row count before and after the GroupBy.
MyWebServiceUrls
.AsParallel()
.SelectMany(url => GetMetricItemData(url))
.GroupBy(item => new { item.DateTime, item.Group, item.Metric }, item => item.Value)
.Select(grp => new MetricItem()
{
DateTime = grp.Key.DateTime,
Group = grp.Key.Group,
Metric = grp.Key.Metric,
Value = // type = decimal?
grp.Any(mi => mi.HasValue)
? grp.Key.Metric.AggregationType == Metric.MetricAggregationTypes.Sum
? grp.Sum(mi => mi.Value)
: grp.Average(mi => mi)
: null
})
.AsEnumerable();
The syntax looks correct based on other examples I have found.
I send this data back to my database and can aggregate with the statement GROUP BY [DateTime], [Group], [Metric] and everything works great. While I can use the database to solve this issue, I would like to know how to correctly use LINQ in this instance.
What am I missing to get this LINQ expression to work?
UPDATE:
This is the relevant MetricItem and Metric class definition:
public class MetricItem
{
public string Group { get; set; }
public DateTime DateTime { get; set; }
public Metric Metric { get; set; }
public Decimal? Value { get; set; }
}
public class Metric
{
public string Code { get; set; }
public string Label { get; set; }
private List<string> SumMetrics = new List<string>(new string[] { "TPI", "TPO", "TPIO" });
public enum MetricAggregationTypes { Sum, Average };
public MetricAggregationTypes AggregationType
{
get
{
if (SumMetrics.IndexOf(this.Code) >= 0)
return MetricAggregationTypes.Sum;
else
return MetricAggregationTypes.Average;
}
}
}
You need to override Equals and GetHashCode on the Metric class. Most Linq methods use hash codes for comparison operations, so for most objects you define yourself, you need to override this class if you plan to use something like GroupBy, Union, etc.

Linq/C# orderby in web api

I'm a little lost here. I've tried multiple different methods for returning this list of names I have but I can't seem to return them in the correct alphabetical order. This is what I've got:
[HttpGet]
[Queryable(PageSize=150)]
public IQueryable<BoyName> GetBoyNames(string letter)
{
List<BoyName> names = Db.BoyNames.Where(c => c.Name.StartsWith(letter)).OrderBy(x => x.Name).ToList();
return names.AsQueryable();
}
And my model:
public partial class BoyName
{
public int Id { get; set; }
public string Name { get; set; }
public string Meaning { get; set; }
public string Origin { get; set; }
}
It is giving me names which is nice...but I can't seem to get them to display in order.
As pointed out in the comments above, calling AsQueryable() on your list object may not preserve the ordering of the list. Rather than converting the result of your LINQ query to a List and then calling AsQueryable, just return the result of your LINQ query since it is a valid return type for your method (IQueryable<BoyName>).
var names = Db.BoyNames
.Where(c => c.Name.StartsWith(letter))
.OrderBy(x => x.Name);
return names;

Categories

Resources