I need some help filtering some data. I've got an object class with three properties. The collection of objects I've got can have many matches of the first property, Point3d. From that collection of matches I need to see if the second property has unique values, Tag. Finally, I need to be able to identify the objects whos Point3d match, and Tags are different, using the third property, it's Id (which is always unique).
class pmatch
{
public string Point3d { get; set; }
public string Tag { get; set; }
public string Id { get; set; }
}
An example of what i'm looking for would be:
List<pmatch> dataset = new List<pmatch>
{
new pmatch { Point3d = "1, 1, 1", Tag = "5", Id = "123" },
new pmatch { Point3d = "1, 1, 1", Tag = "6", Id = "124" },
new pmatch { Point3d = "1, 1, 2", Tag = "7", Id = "125" },
new pmatch { Point3d = "1, 1, 2", Tag = "7", Id = "126" }
};
I need to be able to identify Id's 123 and 124, as their Point3ds match, but their Tags do not. I've been able to identify these instances using LINQ:
var result = datalist.GroupBy(item => item.Point3d, item => item.Tag);
foreach (var group in result)
{
Console.WriteLine(group.Key);
var uniqueTags = group.Distinct().ToList();
if (uniqueTags.Count > 1)
{
Console.WriteLine("Found mismatched tags");
foreach (string Tag in group)
{
Console.WriteLine(" {0}", Tag);
}
}
}
However these results do not give me the Id, so I can not access the object I have identified. How do I get these results along with the Id, or the pmatch object itself?
You can accomplish the desired result like so:
var resultSet =
dataset.GroupBy(item => item.Point3d)
.Where(group => group.Select(item => item.Tag)
.Distinct()
.Count() > 1)
.ToDictionary(item => item.Key, item => item.ToList());
This will identify Id's 123 and 124, as their Point3ds match, but their Tags do not and also resultSet is of type Dictionary<string, List<pmatch>> so you have access to all the properties of the pmatch object.
Related
I want to order a lambda result . I did this:
var dicWords= _context.dicWords
.Select(p=>new WordsDicDto
{
Id=p.Id,
OrderingLetter=p.OrderingLetter,
Word=p.Word,
})
.ToList();
I want to order the list by this condition. if the OrderingLetter is numeric it should be ordered by int.parse(p.OrderingLetter) and if it is not numeric so it should ordered by p,OrderingLetter itself. how should accomplish that?
If I understand your question right, you are looking for something like shown below. The most important place is the CompareTo() method which defines which order two elements have to each other. A negative value means that the current instance (accessed by this.) is preceding obj in sort order. Positive means the opposite. (See also here for the official documentation for CompareTo(): https://learn.microsoft.com/en-us/dotnet/api/system.icomparable.compareto?view=net-6.0#returns)
var dicWords = new List<WordsDicDto>(){
new WordsDicDto() {Id = "1", OrderingLetter = "M", Word = "Mouse" },
new WordsDicDto() {Id = "3", OrderingLetter = "2", Word = "Bycicle"},
null,
new WordsDicDto() {Id = "4", OrderingLetter = "1", Word = "Dog"},
new WordsDicDto() {Id = "2", OrderingLetter = "C", Word = "Car"},
};
Console.WriteLine("The words before sorting:");
dicWords.ForEach(p => Console.WriteLine($"{p?.Id} | {p?.OrderingLetter} | {p?.Word}"));
// This is the filtering
var result = dicWords.OrderBy<WordsDicDto, object>(p => p).ToList();
Console.WriteLine("--");
Console.WriteLine("The words after sorting:");
result.ForEach(p => Console.WriteLine($"{p?.Id} | {p?.OrderingLetter} | {p?.Word}"));
This is the used implementation of WordsDicDto with the implementation of CompareTo():
internal class WordsDicDto : IComparable
{
public string Id { get; set; }
public string OrderingLetter { get; set;}
public string Word { get; set; }
public int CompareTo(object obj)
{
if (obj is WordsDicDto p)
{
if (int.TryParse(this.OrderingLetter, out int instanceValue) &&
int.TryParse(p.OrderingLetter, out int numericValue))
{
return instanceValue - numericValue;
}
else
{
return String.Compare(this.OrderingLetter, p.OrderingLetter, StringComparison.Ordinal);
}
}
else
{
return -1;
}
}
}
I have list of data and user defined sort list and I need to sort my source list based on total numbers defined in the sort list object. It should be dynamic so that the user can freely create his own sort list preference. In my sample code I used Person class. The class may have more properties in the future that's why I want that my sort expression is dynamic too. I used PropertyName to convey a lookup for property. In my example below I have list of person and I have list of sort preference. In my first example I want to sort the person list by Name ascending, then by Age descending. Can someone help me have a LINQ extension? I saw an example in this post Dynamic Linq Order By Variable
The scenario in that post is quite similar to mine except this one is using fixed properties. What I want to achieve is dynamic like the following.
Sort expression is dynamic that is I need to look up for property name that has matching in my sort expression. If any found sort based on sort direction.
Sort execution should be based on how many sort items are defined in the sort list. For example loop through the sort list and do OrderBy (if ascending), OrderByDescending (if descending), ThenBy, ThenBy so on and so fort. For example I have 2 sort order then the source list should be ordered by then by. If I have 5 then the list should sorted in 1 "OrderBy (desc or asc)" and 4 "ThenBy (desc or asc)". The chain should not be broken that for example given 4 sort order and all are ascending it will become persons.OrderBy(prop1).ThenBy(prop2).ThenBy(prop3).ThenBy(prop4).
C# Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SortDemo
{
class Program
{
static void Main(string[] args)
{
var persons = new List<Person>();
persons.Add(new Person { Name="George", Age=25, Group="A", State="LA"});
persons.Add(new Person { Name = "Anna", Age = 20, Group = "B", State = "CA" });
persons.Add(new Person { Name = "Xenna", Age = 30, Group = "A", State = "DC" });
persons.Add(new Person { Name = "Sam", Age = 40, Group = "C", State = "IL" });
persons.Add(new Person { Name = "Elise", Age = 21, Group = "B", State = "LA" });
persons.Add(new Person { Name = "Josh", Age = 29, Group = "C", State = "MI" });
persons.Add(new Person { Name = "Mike", Age = 34, Group = "A", State = "NY" });
persons.Add(new Person { Name = "Bernard", Age = 27, Group = "C", State = "WY" });
var sorts = new List<Sort>();
sorts.Add(new Sort { PropertyName = "Age", SortOrder = 2, Direction = "Descending" });
sorts.Add(new Sort { PropertyName="Name", SortOrder=1, Direction="Ascending"});
//sort by two properties
foreach(var sort in sorts.OrderBy(x=>x.SortOrder))
{
//OrderBy if sort direction is Ascending
//OrderByDescending if sort direction is Descending
var sortedPersons = persons.OrderBy(x=>PropertyName==sort.PropertyName);
//expected results
//order persons by Name ascending
//then by Age Descending
}
//another example
var sorts1 = new List<Sort>();
sorts1.Add(new Sort { PropertyName = "Name", SortOrder = 4, Direction = "Descending" });
sorts1.Add(new Sort { PropertyName = "Age", SortOrder = 1, Direction = "Ascending" });
sorts1.Add(new Sort { PropertyName = "State", SortOrder = 3, Direction = "Ascending" });
sorts1.Add(new Sort { PropertyName = "Group", SortOrder = 2, Direction = "Ascending" });
//sort by four properties
foreach (var sort in sorts1.OrderBy(x => x.SortOrder))
{
//OrderBy if sort direction is Ascending
//OrderByDescending if sort direction is Descending
var sortedPersons1 = persons.OrderBy(x => PropertyName == sort.PropertyName);
//expected results
//order persons by Age Ascending
//then by Group Ascending
//then by State Ascending
//then by Name Descending
}
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Group { get; set; }
public string State { get; set; }
}
public class Sort
{
public string PropertyName { get; set; }
public int SortOrder { get; set; }
public string Direction { get; set; }
}
}
This is my original answer which has IQueryable implementation - good for LINQ to Entities queries, but also can be performant for LINQ to Objects.
In your case it can be used in this way:
var sorts = new List<Sort>();
sorts.Add(new Sort { PropertyName = "Age", SortOrder = 2, Direction = "Descending" });
sorts.Add(new Sort { PropertyName = "Name", SortOrder = 1, Direction = "Ascending"});
var orderByInfo = sorts.OrderBy(s => s.SortOrder)
.Select(s => Tuple.Create(s.PropertyName, s.Direction == "Descending"));
var sordedPersons = persons.AsQueryable().ApplyOrderBy(orderByInfo).ToList();
Try this to sort an in-memory list.
List<Person> SortDynamically(IEnumerable<Person> persons, IList<Sort> sorts)
{
// this line to get an IOrderedEnumerable<T> so that we can chain ThenBy(s)
var sortedPersons = persons.OrderBy(x => 1);
foreach(var sort in sorts.OrderBy(x => x.SortOrder))
{
sortedPersons = sort.Direction switch
{
"Ascending" => sortedPersons
.ThenBy(x => x.GetType().GetProperty(sort.PropertyName)?.GetValue(x, null)),
"Descending" => sortedPersons
.ThenByDescending(x => x.GetType().GetProperty(sort.PropertyName)?.GetValue(x, null)),
_ => throw new ArgumentException("Sort Direction must be Ascending or Descending")
};
}
return sortedPersons.ToList();
}
Alternatively, if you do not like the persons.OrderBy(x => 1) trick, you could the call OrderBy and ThenBy separately.
List<Person> SortDynamicallyAlt(IEnumerable<Person> persons, IList<Sort> sorts)
{
if(sorts.Count == 0)
{
return persons.ToList();
}
var firstSort = sorts.OrderBy(x => x.SortOrder).First();
var sortedPersonsAlt = firstSort.Direction switch
{
"Ascending" => persons
.OrderBy(x => x.GetType().GetProperty(firstSort.PropertyName)?.GetValue(x, null)),
"Descending" => persons
.OrderByDescending(x => x.GetType().GetProperty(firstSort.PropertyName)?.GetValue(x, null)),
_=> throw new ArgumentException("Sort Direction must be Ascending or Descending")
};
foreach(var sort in sorts.OrderBy(x => x.SortOrder).Skip(1))
{
sortedPersonsAlt = sort.Direction switch
{
"Ascending" => sortedPersonsAlt
.ThenBy(x => x.GetType().GetProperty(sort.PropertyName)?.GetValue(x, null)),
"Descending" => sortedPersonsAlt
.ThenByDescending(x => x.GetType().GetProperty(sort.PropertyName)?.GetValue(x, null)),
_=> throw new ArgumentException("sort Direction must be Ascending or Descending")
};
}
return sortedPersonsAlt.ToList();
}
I have the following problem: the query with linq works ok, but I need to add the coordinates within an array for each record that is repeated
Model
public class AppPoisModel
{
public int idPoiType { get; set; }
public string sector { get; set; }
public double latPoint { get; set; }
public double lngPoint { get; set; }
}
Query
var result = (from c in db.coord
select new AppPoisModel
{
idPoiType = c.id,
sector = c.sector,
latPoint = c.latitude ?? 0,
lngPoint = c.longitude ?? 0
}).ToList();
Result
[
{
"$id": "1",
"sector" : "A",
"latPoint": 0,
"lngPoint": 0
},
{
"$id": "2",
"sector" : "A",
"latPoint": 0,
"lngPoint": 0
}
]
Desired result
[
{
"$id": "1",
"sector" : "A",
"coords": [{latPoint, lngPoint}, {latPoint, lngPoint}]
}
]
thank you very much for your contributions
looks like you need a group by...
Also you might create a class for Coords, and make AppPoisModel or a new result class, with a coords field typed as a collection of coords
check it out: Group by in LINQ
similar solution https://stackoverflow.com/a/47580961
The initial query you suggested has no clue that there's a common relationship with items in the same sector.
To accomplish this, you'll want to use the Enumerable.GroupBy() method to group those items together with the basis of having the same id and sector together.
If both of those will always be correlated, you can just GroupBy() one of them to make the comparison simpler, but the results will also reflect that.
var result = (from c in db.coord
select new AppPoisModel
{
idPoiType = c.id,
sector = c.sector,
latPoint = c.latitude ?? 0,
lngPoint = c.longitude ?? 0
}).GroupBy(x => new { id = x.idPoiType, sector = x.sector });
In your case, possibly with both id and sector. This will be your key when you want to loop over the results. So that you can morph the results into the data type you want.
I have a list of strings
List<string> listOfStrings = {"1","2","3","4"};
And I have a list of objects which looks like this
class Object A{
string id;
string Name;
}
How can I find all the objects which has matching list of strings.
I tried:
listOfA.Where(x => listoFstrings.Contains(x.id)).Select();
But it is not working, it is pulling all the other objects which doesn't have a matching string.
Here's a compilable, working version of your code:
// Specify list of strings to match against Id
var listOfStrings = new List<string> { "1", "2", "3", "4" };
// Your "A" class
public class A
{
public string Id { get; set; }
public string Name { get; set; }
}
// A new list of A objects with some Ids that will match
var listOfA = new List<A>
{
new A { Id = "2", Name = "A-2" },
new A { Id = "4", Name = "A-4" },
};
Now you should be able to just about use your original code, except instead of .Select() I've used .ToList():
// Search for A objects in the list where the Id is part of your string list
var matches = listOfA.Where(x => listOfstrings.Contains(x.Id)).ToList();
I have a table like this (Groups):
ID Name ParentID
1 Group 1 null
2 Group 2 null
3 SubGr 1-1 1
4 SubGr 1-2 1
5 SubGr 2-1 2
6 Group 3 null
7 SubGr 1-2-1 4
..... and so on
I want to serialize this to JSON looking like this:
[{"id":1,
"name":"Group 1",
"children": [
{
"id":3,
"name":"SubGr 1-1",
"children":null
},{
"id":4,
"name":"SubGr 1-2",
"children": [
{
"id":7,
"name":"SubGr 1-2-1",
"children": null
}
]
}
]
},
{"id":2,
"name":"Group 2",
"children": [
{
"id":5,
"name":"SubGr 2-1",
"children":null
}
]
},
{"id":6,
"name": "Group 3",
"children": null
}
]
As you can see, you can have indefinite subgroups.
How can I make such a query in LINQ and output it in JSON like the example above?
I have no problem outputting the JSON as seperated elements, with ParentID, but I need to have the structure as mentioned above.
This is the code that I am currently working with, after trying different things around, but with no luck still (this version only gives two levels):
public ActionResult GetGroups()
{
var groupobjs = db.GroupObjs.ToList();
var items = groupobjs.Where(p => p.ParentID == null).Select(p => new
{
id = p.ID,
name = p.Name,
children = groupobjs.Where(c => c.ParentID == p.ID).Select(c => new {
id = c.ID,
name = c.Name
})
});
return Json(items, JsonRequestBehavior.AllowGet);
}
I was working on some code similar to what #Hunter-974 recommended.
public class Group
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public List<Group> Subgroups { get; set; }
public Group()
{
this.Subgroups = new List<Group>();
}
}
class Program
{
static void Main()
{
Group[] groups = new Group[]
{
new Group { Id = 1, Name = "Group 1", ParentId = null },
new Group { Id = 2, Name = "Group 2", ParentId = null },
new Group { Id = 3, Name = "SubGr 1-1", ParentId = 1 },
new Group { Id = 4, Name = "SubGr 1-2", ParentId = 1 },
new Group { Id = 5, Name = "SubGr 2-1", ParentId = 2 },
new Group { Id = 6, Name = "Group 3", ParentId = null },
new Group { Id = 7, Name = "SubGr 1-2-1", ParentId = 4 }
};
foreach (Group g in groups)
if (g.ParentId.HasValue)
groups.Single(group => group.Id == g.ParentId.Value).Subgroups.Add(g);
var rootgroups = groups.Where(g => g.ParentId == null);
JavaScriptSerializer js = new JavaScriptSerializer();
Console.WriteLine(js.Serialize(rootgroups));
}
}
I think that you should use a recursive methode instead of LINQ to do this.
1) Define a class which represent a group, with a property ID (int), a property Name (string), a property Children (List), a property Parent (int?, not serialized) and a method Serialize() (who calls Serialize for each Group in Children)
2) Define a List that will contain the "root" groups, and a List that will contain all the groups.
3) For each row of your datatable, create an object Group. Define all its properties. obviously, the list of children will be empty.
4) For each Group, if the parent's ID is not null, add it to his parent. This way, you will fill the Children list, for all the Groups.
5) For each Group, if the parent's ID is null, add the Group to the Roots list.
6) For each Group in the Roots list, call the method Serialize.
I hope this will help you. Ask me if you want more explanation or code instead of them.