C# Linq Groupby and Where - c#

I have a custom list, shown below.
class ActionToDo
{
public string Name {get;set;}
public DateTime dtDate {get;set;}
public string EventCode {get;set;}
public string EventDescription {get;set;}
}
What I would like to do is find items which have the same date & same event description and where the EventCode = "AQ". I am guessing LINQ is the best way to achieve this? I do not know how to do this though using LINQ.
In my head using Sql I think it would roughly be something like below.
SELECT * FROM SomeTable
WHERE [EventDescription] = 'AQ'
GROUP BY [dtDate], [EventDescription]

.Where(x=> x.EventDescription == "AQ")
.GroupBy(x => new { x.dtDate.Date, x.EventDescription})

So let's assume you have a
List<ActionToDo> actionToDo = new List<ActionToDo>();
actionToDo.Where(i => i.EventDescription == "AQ" && i.EventCode="AQ")
.GroupBy(i => new { i.dtDate.Date, i.EventDescription });

Here is a LINQ query expressed using query syntax that meets your requirements. In addition, it also sorts the grouping properties dtDate and EventDescription and then the members of each group by Name:
var actions = new List<ActionToDo>();
// populate 'actions'
var results =
from a in actions
where a.EventCode == "AQ"
orderby a.dtDate, a.EventDescription, a.Name
group a by new { a.dtDate, a.EventDescription };
To demonstrate this query, I created a program with some sample ActionToDo data in random order. Please see below for the formatted output from the program followed by the program itself.
Demonstration Program Output
[2014-02-12] [Desc.AQ.12]
AQ.12a
AQ.12b
[2014-02-13] [Desc.AQ.13]
AQ.13a
AQ.13b
AQ.13c
[2014-02-14] [Desc.AQ.14]
AQ.14a
AQ.14b
Demonstration Program
using System;
using System.Collections.Generic;
using System.Linq;
class GroupByDemo
{
static public void Main(string[] args)
{
var actions = new List<ActionToDo>()
{
new ActionToDo("AQ.14b", "2014-02-14", "AQ", "Desc.AQ.14"),
new ActionToDo("AQ.12a", "2014-02-12", "AQ", "Desc.AQ.12"),
new ActionToDo("AQ.13b", "2014-02-13", "AQ", "Desc.AQ.13"),
new ActionToDo("XX.01", "2014-02-01", "XX", "Desc.XX.01"),
new ActionToDo("AQ.14a", "2014-02-14", "AQ", "Desc.AQ.14"),
new ActionToDo("AQ.12b", "2014-02-12", "AQ", "Desc.AQ.12"),
new ActionToDo("AQ.13a", "2014-02-13", "AQ", "Desc.AQ.13"),
new ActionToDo("XX.02", "2014-02-02", "XX", "Desc.XX.02"),
new ActionToDo("AQ.13c", "2014-02-13", "AQ", "Desc.AQ.13"),
new ActionToDo("XX.03", "2014-02-03", "XX", "Desc.XX.03")
};
var results =
from a in actions
where a.EventCode == "AQ"
orderby a.dtDate, a.EventDescription, a.Name
group a by new { a.dtDate, a.EventDescription };
foreach (var group in results)
{
Console.WriteLine("[{0}] [{1}]",
group.Key.dtDate.ToString("yyyy-MM-dd"),
group.Key.EventDescription);
foreach (var action in group)
{
Console.WriteLine(" {0}", action.Name);
}
}
}
}
class ActionToDo
{
public string Name {get;set;}
public DateTime dtDate {get;set;}
public string EventCode {get;set;}
public string EventDescription {get;set;}
public ActionToDo(
string name,
string dtDateString,
string eventCode,
string eventDescription)
{
this.Name = name;
this.dtDate = DateTime.Parse(dtDateString);
this.EventCode = eventCode;
this.EventDescription = eventDescription;
}
}

Related

How to add multiple values to list C#

Hi I have the following code when I am adding values to a list.
var NoLiftingList = new List<SQLFields>();
SQLFields nolifting = new SQLFields();
nolifting.Field1 = "No lifting";
NoLiftingList.Add(nolifting);
SQLFields extremelifting = new SQLFields();
extremelifting.Field1 = "Up to 100 lbs (extreme lifting)";
NoLiftingList.Add(extremelifting);
How can I simplify this? Instead of initializing a new object all the time.
This is the code for the whole class updated below.
Thanks
You can add to a list, and set properties on a class by using this inline constructor syntax (working example):
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var NoLiftingList = new List<SQLFields>
{
new SQLFields
{
Field1 = "No Lifting"
},
new SQLFields
{
Field1 = "Up to 100lbs (extreme lifting)"
}
};
}
}
public class SQLFields
{
public string Field1 { get; set; }
}
Use Object Initializers with anonymous types
var NoLiftingList = new List<SQLFields>(){
new SQLFields() { Field1 = "No lifting"},
new SQLFields() { Field1 = "Up to 100 lbs (extreme lifting)"}
};
Ref: MSDN Link
Try this
var NoLiftingList = new List<SQLFields>()
{
new SQLFields()
{
Field1 = "No lifting"
},
new SQLFields()
{
Field1 = "Up to 100 lbs (extreme lifting)"
}
};

LINQ select new list, pass parent class to property class

I'm not sure if I'm just searching the wrong thing or not, I just can't find an example of passing a parent class into a property of that class.
I've attempted to create a simple example which you can see below or view it at https://dotnetfiddle.net/uBGWgp.
I simply would like the Column class to know of its own parent Table, passing through the instance of it via the constructor (new Table()). See comment in code example where I wish to do this: /* Pass parent here? */
using System;
using System.Collections.Generic;
using System.Linq;
public class FakeDb{
public class FakeColumn{
public string ColumnName;
}
public class FakeTable{
public string TableName {get;set;}
public IEnumerable<FakeColumn> Columns {get;set;}
}
public IEnumerable<FakeTable> Tables {get;set;}
public FakeDb(){
Tables = new List<FakeTable>(){
new FakeTable(){
TableName = "People",
Columns = new List<FakeColumn>(){
new FakeColumn(){
ColumnName = "FirstName"
},
new FakeColumn(){
ColumnName = "LastName"
}
}
}
};
}
}
public class Table{
public Guid TableGuid = Guid.NewGuid();
public IEnumerable<Column> Columns;
}
public class Column{
private Table _myTable;
public Guid ColumneGuid = Guid.NewGuid();
public Column(Table hostTable){
_myTable = hostTable;
}
}
public class Program
{
public static void Main()
{
var fakeDb = new FakeDb();
var tableList = fakeDb.Tables.GroupBy(p => p.TableName, p => p, (tableName, values) => new {
TableName = tableName,
OtherValues = values
}).Select(p => new Table(){
Columns = p.OtherValues.Select(i => new Column(/* Pass parent here? */)).ToList()
}).ToList();
}
}
Because you are not holding a reference to the new Table object you are creating, there is no way to do it in a single-lined query. However changing it to a multi-lined lambda expression will make this possible. Save your newly created table in a variable and then pass that variable into your Column's constructor.
You will have to do something like this
.Select(p =>
{
var t = new Table();
t.Columns = p.OtherValues.Select(i => new Column(t)).ToList();
return t;
});
Demo: https://dotnetfiddle.net/1kKxMR

Elastic Search to search for words that starts with phrase

I'm trying to create a search function for my website using Elastic Search and NEST. You can see my code below and I get results if I search for complete (and almost comlete) words.
Ie, if I search for "Buttermilk" or "Buttermil" I get a hit on my document containing the word "Buttermilk".
However, what I try to accomplish is if I search for "Butter", I should have a result with all three documents which have words that starts with "Butter". I thought this was solved by using FuzzyLikeThis?
Can anyone see what I'm doing wrong and point me in the right direction?
I created a console-app and the complete code you can see here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nest;
using Newtonsoft.Json;
namespace ElasticSearchTest
{
class Program
{
static void Main(string[] args)
{
var indexSettings = new IndexSettings();
indexSettings.Analysis.Analyzers["text-en"] = new SnowballAnalyzer { Language = "English" };
ElasticClient.CreateIndex("elastictesting", indexSettings);
var testItem1 = new TestItem {
Id = 1,
Name = "Buttermilk"
};
ElasticClient.Index(testItem1, "elastictesting", "TestItem", testItem1.Id);
var testItem2 = new TestItem {
Id = 2,
Name = "Buttercream"
};
ElasticClient.Index(testItem2, "elastictesting", "TestItem", testItem2.Id);
var testItem3 = new TestItem {
Id = 3,
Name = "Butternut"
};
ElasticClient.Index(testItem3, "elastictesting", "TestItem", testItem3.Id);
Console.WriteLine("Write search phrase:");
var searchPhrase = Console.ReadLine();
var searchResults = Search(searchPhrase);
Console.WriteLine("Number of search results: " + searchResults.Count());
foreach (var item in searchResults) {
Console.WriteLine(item.Name);
}
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
private static List<TestItem> Search(string searchPhrase)
{
var query = BuildQuery(searchPhrase);
var result = ElasticClient
.Search(query)
.Documents
.Select(d => d)
.Distinct()
.ToList();
return result;
}
public static ElasticClient ElasticClient
{
get
{
var localhost = new Uri("http://localhost:9200");
var setting = new ConnectionSettings(localhost);
setting.SetDefaultIndex("elastictesting");
return new ElasticClient(setting);
}
}
private static SearchDescriptor<TestItem> BuildQuery(string searchPhrase)
{
var querifiedKeywords = string.Join(" AND ", searchPhrase.Split(' '));
var filters = new BaseFilter[1];
filters[0] = Filter<TestItem>.Bool(b => b.Should(m => m.Query(q =>
q.FuzzyLikeThis(flt =>
flt.OnFields(new[] {
"name"
}).LikeText(querifiedKeywords)
.PrefixLength(2)
.MaxQueryTerms(1)
.Boost(2))
)));
var searchDescriptor = new SearchDescriptor<TestItem>()
.Filter(f => f.Bool(b => b.Must(filters)))
.Index("elastictesting")
.Type("TestItem")
.Size(500);
var jsons = JsonConvert.SerializeObject(searchDescriptor, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
return searchDescriptor;
}
}
class TestItem {
public int Id { get; set; }
[ElasticProperty(Analyzer = "text-en", Index = FieldIndexOption.analyzed)]
public string Name { get; set; }
}
}
Edited 2014-04-01 11:18
Well, I ended up using MultiMatch and QueryString, so this it how my code looks now. Hope it mey help anyone in the furure. Also, I added a Description property to my TestItem to illustrate multimatch.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nest;
using Newtonsoft.Json;
namespace ElasticSearchTest
{
class Program
{
static void Main(string[] args)
{
var indexSettings = new IndexSettings();
ElasticClient.CreateIndex("elastictesting", indexSettings);
var testItem1 = new TestItem {
Id = 1,
Name = "Buttermilk",
Description = "butter with milk"
};
ElasticClient.Index(testItem1, "elastictesting", "TestItem", testItem1.Id);
var testItem2 = new TestItem {
Id = 2,
Name = "Buttercream",
Description = "Butter with cream"
};
ElasticClient.Index(testItem2, "elastictesting", "TestItem", testItem2.Id);
var testItem3 = new TestItem {
Id = 3,
Name = "Butternut",
Description = "Butter with nut"
};
ElasticClient.Index(testItem3, "elastictesting", "TestItem", testItem3.Id);
Console.WriteLine("Write search phrase:");
var searchPhrase = Console.ReadLine();
var searchResults = Search(searchPhrase);
Console.WriteLine("Number of search results: " + searchResults.Count());
foreach (var item in searchResults) {
Console.WriteLine(item.Name);
Console.WriteLine(item.Description);
}
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
private static List<TestItem> Search(string searchPhrase)
{
var query = BuildQuery(searchPhrase);
var result = ElasticClient
.Search(query)
.Documents
.Select(d => d)
.Distinct()
.ToList();
return result;
}
public static ElasticClient ElasticClient
{
get
{
var localhost = new Uri("http://localhost:9200");
var setting = new ConnectionSettings(localhost);
setting.SetDefaultIndex("elastictesting");
return new ElasticClient(setting);
}
}
private static SearchDescriptor<TestItem> BuildQuery(string searchPhrase)
{
var searchDescriptor = new SearchDescriptor<TestItem>()
.Query(q => q
.MultiMatch(m =>
m.OnFields(new[] {
"name",
"description"
}).QueryString(searchPhrase).Type(TextQueryType.PHRASE_PREFIX)
)
)
.Index("elastictesting")
.Type("TestItem")
.Size(500);
var jsons = JsonConvert.SerializeObject(searchDescriptor, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
return searchDescriptor;
}
}
class TestItem {
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}
Instead of using FuzzyLikequery.. use prefix query its more fast and accurate..!
for more information refer
curl -XPOST "http://localhost:9200/try/indextype/_search" -d'
{
"query": {
"prefix": {
"field": {
"value": "Butter"
}
}
}
}'
create above query in NEST and try again..!
This has nothing to do with FuzzyLikeThis.
You can use prefixquery as suggested by #BlackPOP out of the box.
You could also opt for using EdgeNGrams, this will tokenize your input on index-time. The result faster performance as compared to prefixquery, offset against increased index size.
One thing to keep in mind is that prefixquery only works on non-analyzed fields, so if you want to do any anaylzing at indexing-time, you're probably better off using EdgeNGrams.
Please read up on anaylzers etc, if you don't know what they are.
Some refs:
http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/analysis-analyzers.html
http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/analysis-edgengram-tokenizer.html
See How can I do a prefix search in ElasticSearch in addition to a generic query string? for a similar question.

Generic custom linq filter

How can I create a custom generic linq filter that will check if the generic class contains a property name and returns the query...
I have something like:
public static IQueryable<T> GetAllByType<T>(
this IQueryable<T> customQuery, string seller) where T : class, new()
{
customQuery = customQuery.Where(i => i.GetType().Name == "TypeOfSeller");
return customQuery;
}
If the property type on the table T exists then I want to filter the expression using the string seller passed in as a parameter...
simply: return an expression which will filter this table by the seller param which could be "big", "small" etc.
I think this does what you describe.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
class Seller
{
public string Name { get; set; }
public string TypeOfSeller { get; set; }
}
class SomeOtherData
{
public string Name { get; set; }
}
static class Program
{
static void Main(string[] args)
{
List<Seller> sellers = new List<Seller>();
sellers.Add(new Seller() { Name = "A", TypeOfSeller = "Test" });
sellers.Add(new Seller() { Name = "B", TypeOfSeller = "Test" });
sellers.Add(new Seller() { Name = "C", TypeOfSeller = "Other" });
var q = from p in sellers.AsQueryable<Seller>().GetAllByType("Test") select p;
List<SomeOtherData> other = new List<SomeOtherData>();
other.Add(new SomeOtherData() { Name = "A" });
other.Add(new SomeOtherData() { Name = "B" });
other.Add(new SomeOtherData() { Name = "C" });
var q2 = from p in other.AsQueryable<SomeOtherData>().GetAllByType("Test") select p;
foreach (var x in q)
Console.WriteLine(x.Name + ", " + x.TypeOfSeller);
Console.WriteLine("Other Data: ");
foreach (var x in q2)
Console.WriteLine(x.Name);
Console.ReadLine();
}
public static IQueryable<T> GetAllByType<T>(this IQueryable<T> customQuery, string seller) where T : class, new()
{
var prop = typeof(T).GetProperty("TypeOfSeller");
if(prop != null)
customQuery = customQuery.Where(i => prop.GetValue(i, new object[] {}).ToString() == seller);
return customQuery;
}
}
}
The output is:
A, Test
B, Test
Other Data:
A
B
C
I would refactor it a bit so that there's no "if" involved but that I'm only sending the entities that qualify to the method.
The next thing you want to consider is that if there are multiple entity models that share a property name and you want to share logic regarding that property name, utilize the partial aspect of the code-generated classes to extend those classes and have each of them implement an interface.
interface ISeller
{
string TypeOfSeller { get; set; }
}
This will allow you to add the interface to the list of constraints on the method and also allow you to simply utilize the TypeOfSeller property directly without trying to resort to other methods (such as reflection).
The question is not very clear but sounds like you want something like this
public static IQueryable<T> GetAllByType<T>(
this IQueryable<T> customQuery, string seller) where T : class, new()
{
return from i in customQuery
let prop = typeof(T).GetProperty("SellerType")
where prop != null && prop.GetValue(i, null).Equals(seller)
select i;
}

List Manipulation in C# using Linq

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace ConsoleApplication1
{
public class Class1
{
static void Main(string[] args)
{
List<Car> mylist = new List<Car>();
Car car1;
Car car2;
Car car3;
car1 = new Car()
{
make = "Honda",
id = 1
};
car2 = new Car()
{
make = "toyota",
id = 2
};
car3 = new Car()
{
make = "Honda",
id = 3,
color = "red"
};
mylist.Add(car1);
mylist.Add(car2);
**////mylist.Where(p => p.id == 1).SingleOrDefault() = car3;**
}
}
public class Car
{
public int id { get; set; }
public string make { get; set; }
public string color { get; set; }
}
}
How can I update the list by replacing the honda car of Id 1 with honda car with Id 3 in the best way.
Everything leppie said - plus:
int index = mylist.FindIndex(p => p.id == 1);
if(index<0) {
mylist.Add(car3);
} else {
mylist[index] = car3;
}
This just uses the existing FindIndex to locate a car with id 1, then replace or add it. No LINQ; no SQL - just a lambda and List<T>.
If you wanted to do an update to multiple elements...
foreach (var f in mylist.FindAll(x => x.id == 1))
{
f.id = car3.id;
f.color = car3.color;
f.make = car3.make;
}
This is not LINQ2SQL.
Also, LINQ is not used for updating, only to query for objects.
You can use this way :
(from car in mylist
where car.id == 1
select car).Update(
car => car.id = 3);
My reference is this website. Or following is the code for Update method
public static void Update<T>(this IEnumerable<T> source, params Action<T>[] updates)
{
if (source == null)
throw new ArgumentNullException("source");
if (updates == null)
throw new ArgumentNullException("updates");
foreach (T item in source)
{
foreach (Action<T> update in updates)
{
update(item);
}
}
}
As Leppie said, LINQ is for querying rather than updating. However, that can be used to build a new list:
mylist = new List<Car>(from car in mylist select car.id == 1? car3 : car)
That is if you want to use LINQ. It's nice and short code, of course, but a bit less efficient than Marc Gravell's suggestion, as it effectively creates a new list, rather than updating the old one.
//Item class
Class Item
{
public string Name { get; set; }
}
List < Item > myList = new List< Item >()
//Add item to list
Item item = new Item();
item.Name = "Name";
myList.Add(Item);
//Find the item with the name prop
Item item2 = myList.Find(x => x.Name == "Name");
if(item2 != null)
item.Name = "Changed";
Just one question, why do I have to write a Update function for something that seems so basic for a list? There should be standard methods for Lists like Add(), Delete(), Edit(), Insert(), Replace() ....Find()
List<AvailabilityIssue> ai = new List<AvailabilityIssue>();
ai.AddRange(
(from a in db.CrewLicences
where
a.ValidationDate <= ((UniversalTime)todayDate.AddDays(30)).Time &&
a.ValidationDate >= ((UniversalTime)todayDate.AddDays(15)).Time
select new AvailabilityIssue()
{
crewMemberId = a.CrewMemberId,
expirationDays = 30,
Name = a.LicenceType.Name,
expirationDate = new UniversalTime(a.ValidationDate).ToString("yyyy-MM-dd"),
documentType = Controllers.rpmdataController.DocumentType.Licence
}).ToList());

Categories

Resources