Linq, force an item to be first? - linq

I have a List<> of MyPersonObjects. The list is a list of people who I can assign something to. In this list, I include myself (As the task can be assigned to me - and usually is).
So, I want to make sure myself is at the top of the list. I know my personId, which is a property of my person object - so is there a way to order the list to make sure I am first, and then the rest, alphabetically by surname (which is another property of my object)

You can just order the list by two separate criteria, the OrderBy will be the main sort order, if the values are equal (1 for all except you), ThenBy is used.
personList.OrderBy(x => x.Id == myId ? 0 : 1)
.ThenBy(x => x.Surname);

One way is to sort the list by surname with Linq and then remove yourself and put back at the first place:
var personList = db.MyPersonObjects.OrderBy(p => p.Surname).ToList();
var myself = personList.Single(p => p.Id == myselfId);
personList.Remove(myself);
personList.Insert(0, myself);
Or create a custom comparer IComparer<MyPersonObject> where the implementation is something like:
public class PersonCompaper : IComparer<MyPersonObject>
{
private readonly int personId;
public PersonCompaper(int personId)
{
this.personId = personId;
}
public int Compare(MyPersonObject x, MyPersonObject y)
{
if (x.Id == personId && y.Id == personId)
return 0;
if (x.Id == personId)
return 1;
if (y.Id == personId)
return -1;
return string.Compare(x.Surname, y.Surname);
}
}
Then you use it in the OrderBy
db.MyPersonObjects.OrderBy(p => p, new PersonCompaper(myselfId))

Related

How to get all items with the same value in list of lists c# LINQ?

I have a to add a specific requirement in a piece of code already implemented.
The data structure is something of this sort:
public class Module
{
public string Type;
public string ID;
public List<Point> Points = new List<Point>();
}
public class Point
{
public string Type;
public string Location;
public string Connection;
}
Originally LINQ was used to return all modules which certain characteristics
List<Module> miList = Modules.Where(m => m.Type != null
&& m.ID == "A"
&& m.Points.Where(t => t.Connection == ""
&& SimilarPoint(t.Type, x).ToList())
.Count() > 0)
.ToList();
with x an input to the function. The new requirement dictates that the modules returned shall all have points with Connection equal to "" and the same value in the Location field.
It seemed to me that the SelectMany could be used to this end, but I am not getting what I expected.
How should the function above be modified?
Thanks in advance
Not exactly sure what the SimilarPoint(t.Type, x) does here.
May be you should try something like this and find out if it works for you -
var resultSet = Modules.Where(m => !string.IsNullOrEmpty(m.Type) && m.ID.Equals("A"))
.Select(n =>
new Module {
Type=n.Type,
ID=n.ID,
Points= n.Points.Where(p => String.IsNullOrEmpty(p.Connection) && String.IsNullOrEmpty(p.Location)).ToList()
})
.ToList();
You said all the returned modules have the same Location, but that doesn't explain how you select which Location so I arbitrarily picked the first matching module's location:
var miQuery1 = Modules.Where(m => m.Type != null
&& m.ID == "A"
&& m.Points.Where(t => t.Connection == ""
&& SimilarPoint(t.Type, x).ToList()).Count() > 0)
.Where(m => m.Points.All(p => p.Connection == ""));
var miQuery2 = miQuery1.Where(m => m.Location == miQuery1.First().Location);
List<Module> miList = miQuery2.ToList();

enumerable group field using Linq?

I've written a Linq sentence like this:
var fs = list
.GroupBy(i =>
new {
X = i.X,
Ps = i.Properties.Where(p => p.Key.Equals("m")) <<<<<<<<<<<
}
)
.Select(g => g.Key });
Am I able to group by IEnumerable.Where(...) fields?
The grouping won't work here.
When grouping, the runtime will try to compare group keys in order to produce proper groups. However, since in the group key you use a property (Ps) which is a distinct IEnumerable<T> for each item in list (the comparison is made on reference equality not on sequence equality) this will result in a different collection for each element; in other words if you'll have two items:
var a = new { X = 1, Properties = new[] { "m" } };
var b = new { X = 1, Properties = new[] { "m" } };
The GroupBy clause will give you two distinct keys as you can see from the image below.
If your intent is to just project the items into the structure of the GroupBy key then you don't need the grouping; the query below should give the same result:
var fs = list.Select(item => new
{
item.X,
Ps = item.Properties.Where(p => p.Key == "m")
});
However, if you do require the results to be distinct, you'll need to create a separate class for your result and implement a separate IEqualityComparer<T> to be used with Distinct clause:
public class Result
{
public int X { get; set; }
public IEnumerable<string> Ps { get; set; }
}
public class ResultComparer : IEqualityComparer<Result>
{
public bool Equals(Result a, Result b)
{
return a.X == b.X && a.Ps.SequenceEqual(b.Ps);
}
// Implement GetHashCode
}
Having the above you can use Distinct on the first query to get distinct results:
var fs = list.Select(item => new Result
{
X = item.X,
Ps = item.Properties.Where( p => p.Key == "m")
}).Distinct(new ResultComparer());

Linq: Using Distinct on calculated result

I have a query which calculates a range of dates which I want to run a distinct query on to return just Year/Month combinations. I can't work out how best to achieve this.
private IEnumerable<DateTime> MonthsWithItineraryData (int Id) // shipId
{
var months = (from i in context.Itineraries
join isd in context.ItineraryStartDates on i.Id equals isd.ItineraryId
join id in context.ItineraryDay on i.Id equals id.ItineraryId
where i.ShipId == Id
select (DateTime)DbFunctions.AddDays(isd.Date, id.Day)
);
return (months);
}
I think you want something like this:
return monts.GroupBy(x => new {x.Month, x.Year})
.Select(x => x.First());
or else you can a custom equality comparer as distinct argument:
public class DateTimeComparer : IEqualityComparer<DateTime>
{
public bool Equals(DateTime x, DateTime y)
{
return x.Year == y.Year && x.Month == y.Month;
}
public int GetHashCode(DateTime obj)
{
return obj.Year * 100 + obj.Month;
}
}
result = months.Distinct(new DateTimeComparer()).ToList();

LINQ distinct selection based on a property value

I have a generic list of countries completely filled with countries instances.
List <Country> mylist
This list has different instances of countries but some has the same value for the property "name".
How could i make a distinct over the property of the country "name" to get only the countries with different names?
Thanks.
Greets.
Jose.
Jon suggested MoreLINQ, which is obviously fine, but maybe you want to avoid another dependency. In this case, you can use Enumerable.Distinct with your own IEqualtyComparer<Country>:
var distinctCountries = myList.Distinct(new EqualityComparerForCountryByName());
//IEqualityComparer
class EqualityComparerForCountryByName : IEqualityComparer<Country> {
public bool Equals(Country x, Country y) {
if(Object.ReferenceEquals(x, y)) { return true; }
if(x == null || y == null) { return false; }
return x.Name == y.Name;
}
public int GetHashCode(Country obj) {
if(obj == null) { return 0; }
return obj.Name.GetHashCode();
}
}
You could use MoreLINQ with its DistinctBy method:
var distinctCountries = allCountries.DistinctBy(c => c.Name);
(You don't have to take all of MoreLINQ of course - you could just copy that one method into your code along with the ThrowIfNull extension method, and preserve the licence text appropriately.)

How do I select records and summary in one query?

The scenario is like this:
name | val
'aa' | 10
'bb' | 20
'cc' | 30
*********
sum | 60
For now I just select all the records in simple LINQ query and invoke the enumerator (ToList())
Then I loop over the list and summarize the val column.
Is there a better way? LINQ selects all to a new typed object so I dont know how to add the additional data.
thanks.
Anonymous type cant allow value to be added or edited once its created. so instead of returning anonymous type, you can use your custom output class. Something like this
public class ResClass
{
public string name;
public int value;
}
public class OutClass
{
public int sum;
public List<ResClass> lstData;
}
int sum=0;
var outtt = objTT.Where(x => x.id == 1).Select(x =>
{
sum += x.value;
return new ResClass { name = x.name, value= x.value };
}).ToList();
OutClass outCls = new OutClass { sum = sum, lstData = outtt };

Resources