Grouping including non grouped field using Linq/Lambda - c#

I have a list which has data
list1 [{actId: 1, dt:'10/5/2015', hr:3}, {actId: 1, dt:'10/5/2015', hr:5},
{actId: 3, dt:'10/4/2015', hr:3}, {actId: 4, dt:'10/6/2015', hr:1},
{actId: 4, dt:'10/6/2015', hr:8}, {actId: 1, dt:'10/2/2015', hr:3}]
I am using a Linq query to group the data
var dat= list1.AsEnumerable().GroupBy(t=> new{t.actId, t.dt})
.Select(x=> new
{
tId=x.Key.techId,
dat=x.Key.dt,
thrs=x.Sum(y=>y.hr)
});
This works and gives me result but gives me the results grouping both "actId" and "dt" while I want to just group them by "actId". If I change the query to
var dat= list1.AsEnumerable().GroupBy(t=> new{t.actId})
.Select(x=> new
{
tId=x.Key.techId,
dat=x.dt,
thrs=x.Sum(y=>y.hr)
});
I get intellisense error for x.dt saying "Cant Resolve Symbol "dt"
Please let me know how to change the query so I can include x.dt in it without grouping by it.
So output should look like
[ {actId:1, [{dt: 10/5/2015, hr:8}, {dt: 10/2/2015, hr:38}]},
{actId: 3, [{dt: 10/4/2015, hr:3}]},
{actId: 4 [{dt: 10/6/2015 hr: 9}]}]
Thanks

It's because there isn't a single 'dt', there is a group of them, this will return them all as a group for you:
var dat=list1.AsEnumerable()
.GroupBy(t=> t.actId)
.Select(x=> new
{
actId=x.Key,
dat=x.Select(x=>new { x.dt,x.hr}).ToList()
});
This is as close as I can get to your output. It'll look like:
[ {actId:1, dat:[{dt: 10/5/2015, hr:8}, {dt: 10/2/2015, hr:38}]},
{actId: 3, dat:[{dt: 10/4/2015, hr:3}]},
{actId: 4, dat:[{dt: 10/6/2015 hr: 9}]}]

Related

Extract the last element and add it to the end in list by changing a property value

I have a list like this
CustomerSearch.cs
int id;
string searchedWord;
If for example, if this result is like this
[
{id: 1, searchedWord :"test"}
{id: 2, searchedWord: "news"}
]
I am trying to copy the last element in the list and append to the existing list and increment the id number value.
The expected result is
[
{id: 1, searchedWord :"test"}
{id: 2, searchedWord: "news"}
{id: 3, searchedWord: "news"}
]
I could do this way
CustomerSearch customerSearch=new CustomerSearch();
customerSearch.Id=result.ElementAt(result.Count()-1).Id;
customerSearch.SearchedWord=result.ElementAt(result.Count()-1).SearchedWord;
List<CustomerSearch> newResult=new List<CustomerSearch>();
foreach(var mycustomerSearch in result)
{
newResult.Add(mycustomerSearch);
}
newResult.Insert(1, customerSearch);
newResult.ElementAt(2).Id=3;
I don't think, this is the optimal way, if there is a better way to do this, please share it, thanks. We can create the expected result as a new list or with that existing list.
Assuming you may modify the original list result:
CustomerSearch customerSearch = new CustomerSearch {
Id = result.Last().Id + 1,
SearchedWord = result.Last().SearchedWord
};
result.Add(customerSearch);
Use Linq Last() to access the last element of result.
Use Object Initializers to initialize the new element nicely.
If you may not modify result:
CustomerSearch customerSearch = new CustomerSearch {
Id = result.Last().Id + 1,
SearchedWord = result.Last().SearchedWord
};
List<CustomerSearch> newResult = result.Select(x => x).ToList();
newResult.Add(customerSearch);
Use Linq Select() followed by ToList() to clone the original list.
Note that Last() throws an exception if result is null or empty. To protect against this, the above operation should be preceded by a check for those cases:
if (results == null || !results.Any())
{
// handle in some way, perhaps by returning.
}
Use Linq Any() to check if result is empty.

How to GroupBy using LINQ query in C# when the key is to be the same type as your type T when grouping List<T>

I have a class as follows
class Employee
{
string emp_id;
string emp_name;
string manager_id;
bool isManager;
}
Data in my List is as follows
[1, Alex, 5, false]
[2, Charlie, 6, false]
[3, Aaron, 5, false]
[4, Brian, 6, false]
[5, Rock, "", true]
[6, William, "", true]
As you notice from above data the first 4 employees are not managers and hence have their manager_id populated but the last 2 are managers and hence do not have a manager_id. It can also be noticed by the bool values.
Now this should be grouped such that the employees under the common manager are grouped together and the key of that group should be their manager.
So based on the data it should group as follows (I am just typing names below for simplicity, in reality they all will be Employee Class Objects)
Group-1
Rock
Alex
Aaron
Group-2
William
Charlie
Brian
How do I achieve this in C# with LINQ?
var listOfCategories = new List<Employee>();
PopulateData(listOfCategories );
var listGroup = listOfCategories .GroupBy(x => x.manager_id);
But the key is now the Manager_id string. One way to get manager I can use Find and get the Employee object based on id.
I believe this can be achieved by LINQ.
I would be glad if someone can point me in the right direction.
Thanks in Advance.
This is probably what you're looking for:
var listGroup = listOfCategories.GroupBy(x => listOfCategories.FirstOrDefault(y => y.emp_id == x.manager_id).emp_name);
Or if you want the keys to be full employee objects, you can remove .emp_name part from the lambda expression.

How would I achieve a unique result using the Linq method syntax

I have the following data in a table:
e.g. data
0, 'Ford'
1, 'Toyota, Toyota'
2, 'BMW'
3, 'Porsche, Porsche'
4, 'BMW'
I need to place this data in the following type List<Tuple<int, string>> carList so that the results within my list would appear as follows:
0, 'Ford'
1, 'Toyota'
2, 'BMW'
3, 'Porsche'
4, 'BMW'
using the following pseudo code:
while (SQLiteDataReader.Read())
{
carList.Add
(
new Tuple<int, string> (
SQLiteDataReader.GetInt32(0) ,
SQLiteDataReader.GetString(1).[Some extension method to produce unique items]
);
)
}
Note, when there are items with duplication (Toyota, Porsche) , the duplication will always be the same name. i.e you won't get something like 'Toyota, Ford'.
Is there some extension method that would remove the duplication part?
This should do the trick:
SQLiteDataReader.GetString(1).Split(',').First().Trim()
If you're looking to do the whole thing through a linq query, this should work.
If you're just looking to fix your pseudocode, then scottm's solution should work.
LinqDataContext db = new LinqDataContext();
List<Tuple<int, string>> results =
db.Cars.AsEnumerable()
.Select(c => Tuple.Create(c.id, c.Make.Split(',')[0].Trim()))
.ToList();

How to select the first three elements of a IEnumerable object?

That's it. The question is in the title
I was looking for a cleaner way than the using for...break;
thanks
This should do it!
var items = new List<int>(){ 1, 2, 3, 4, 5 };
var results = items.Take(3);

Is there a C# equivalent to C++'s std::set_difference?

If so, what is it?
EDIT: In response to comment below:
var tabulatedOutputErrors = from error in outputErrors
group error by error into errorGroup
select new { error = errorGroup.Key, number = errorGroup.Count() };
var tabulatedInputErrors = from error in inputErrors
group error by error into errorGroup
select new { error = errorGroup.Key, number = errorGroup.Count() };
var problems = tabulatedOutputErrors.Except(tabulatedInputErrors);
You can expand out the counts if you need to.
LINQ has the Enumerable.Except extension method, which seems to be what you're looking for.
Example:
var list1 = new int[] {1, 3, 5, 7, 9};
var list2 = new int[] {1, 1, 5, 5, 5, 9};
var result = list1.Except(list2); // result = {3, 7}
Alternative:
From .NET 3.5 onwards there also exists the HashSet<T> class (and also the similar SortedSet<T> class in .NET 4.0. This class (or rather the ISet<T> interface in .NET 4.0) has an ExceptWith method which could also do the job.
Example:
var set1 = new HashSet<int>() {1, 3, 5, 7, 9};
var set2 = new HashSet<int>() {1, 1, 5, 5, 5, 9};
set1.ExceptWith(set2); // set1 = {3, 7}
Of course, it depends on the context/usage whether this approach is more desirable. The efficiency benefit (doing the difference operation in-place and using hash codes) in most cases is probably negligible. Either way, take your pick. :)

Categories