Linq select many into new column - c#

I am trying to make a jquery auto complete where I use a label and value like in this post which means that I need my json in the form of
{ label: 'label text', value: 'value text' }
However I am filtering a list of Employees which is a class with the following structure:
public sealed class Employee
{
public string Name { get; set; }
public string PersonnelNumber { get; set; }
public int RecID { get; set; }
public string Email { get; set; }
}
So I tried the following Linq to get the format of label, value I needed:
var jsonResult = employees
.SelectMany(emp => new { label = emp.Name, value = emp.RecID })
.ToList();
Where employees is a list of Employee objects but it is throwing up an build error of
Error 1 The type arguments for method 'System.Linq.Enumerable.SelectMany(System.Collections.Generic.IEnumerable, System.Func>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
How do I fix this to get the Name and RecID in a new list of objects with label and value as their output?

I think you just want to use Select here:
var jsonResult = employees
.Select(emp => new { label = emp.Name, value = emp.RecID })
.ToList();

SelectMany is for "flattening" a group of collections. Since you just have a single collection just use Select:
var jsonResult = employees.Select(emp => new { label = emp.Name, value = emp.RecID })
.ToList();

Related

Making sure a value is equal to one of many in LINQ

I have an IEnumerable containing values like "ABC", "DEF", etc.
I'm trying to form a LINQ query where the value of a property in my object may be equal to one of the many values contained in my IEnumerable. How do I form that LINQ query?
var statesFilter = new List<string>();
statesFilter.Add("NY");
statesFilter.Add("CA");
var employees = new List<Employee>();
employees = getDataFromSomewhere();
// Code below is not working. Just wanted to give you an idea about my LINQ query
var myFilteredList = employees.Where(x => x.State.Contains(statesFilter));
Employee class could be something like this:
public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string State { get; set; }
}
If State property is of type string, you should change your Where to the following:
var myFilteredList = employees.Where(x => statesFilter.Contains(x.State));
Also, you can do this:
var myFilteredList = employees.Where(x => statesFilter.Any(s=>s==x.State));
Condition should be
x=>statesFilter.Contains(x.State))

LINQ Sum list of items grouped by type inside list

I have the following order object which contains a list of order addons. I am trying to create a report that shows all the addon types and their quantities summed.
public class Order {
public IList<OrderAddon> OrderAddons { get; set; }
}
public class OrderAddon {
public enum OrderType { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
}
This is where I am at and can't figure out if the entire query is wrong of I am just missing something.
var query = from order in Model.Orders
from addon in order.OrderAddons
group order by addon.AddonType
into orderAddons select new
{
Name = orderAddons.Key,
Quantity = orderAddons.Sum(x => x.) << This is where I am stuck
};
When I hit . my intellisense is showing me properties in order object not the addon object.
That's because you're saying group order by ..., so the orderAddons object becomes a grouping of orders. You can use this if you're going to need properties from both objects:
from order in Model.Orders
from addon in order.OrderAddons
group new{addon, order} by addon.AddonType
into orderAddons select new
{
Name = orderAddons.Key,
Quantity = orderAddons.Sum(x => x.addon.Quantity)
};
If this is all the data you need, this is a little simpler:
from order in Model.Orders
from addon in order.OrderAddons
group order.Quantity by addon.AddonType
into quantityByAddonType select new
{
Name = quantityByAddonType.Key,
Quantity = quantityByAddonType.Sum()
};
an alternative syntax same result...
var result = Model.Orders
.SelectMany(order => order.OrderAddons)
.GroupBy(addon => addon.OrderType)
.Select(grouping => new
{
Name = grouping.Key,
Quantity = grouping.Sum(addon => addon.Quantity)
});

Lambda expression - select single object with a IEnumerable<> property

Is it possible to select a single object and populate a containing IEnumerable property with a single Lambda expression?
Something like this:
var someViewModel = _repository.Table.Where(x => x.Id == someId)
.Select(new ListViewModel(){
GroupId = x.Group.Id,
GroupTitle = x.Group.Title
List = ?? // Select new SubViewModel and add it to IEnumerable<SubViewModel>
})
The result I'm after is a new object (ListViewModel in this case) that contains 3 properties. "List" being a collection of newly selected objects.
Is this possible? Am I coming at this from the wrong angle?
Thanks!
Update:
Let me try again :) Keep in mind that my naming is fictional here. Given the following two classes I would like to construct a DB query using a Lambda expression which creates a single "ListViewModel" that contains a collection of "SubViewModel". Does this help clarify?
public class SubViewModel
{
public int Id { get; set; }
public string Title { get; set; }
}
public class ListViewModel
{
public int GroupId { get; set; }
public string GroupTitle { get; set; }
public IEnumerable<SubViewModel> List { get; set; }
}
I'm not sure if I understand your question correctly but here is what I am thinking, you need to create a new IEnumberable and add the item to that collection.
var someViewModel = _repository.Table.Where(x => x.Id == someId)
.Select(new ListViewModel()
{
GroupId = x.Group.Id,
GroupTitle = x.Group.Title
List = new List<SubViewModel> { new SubViewModel(x) }
});

C# Linq "GroupBy"

I have a problem with a code that i wrote today.
I want to select some rows from the database and group them by one of the columns.
This is my code:
public class Classes
{
public IQueryable<ClassData> HomeWorkList { get; set; }
}
public class ClassData
{
public string Name { get; set; }
public string English { get; set; }
}
var classesCustomViews = new Classes {
HomeWorkList = _repositoryClasses.GetRecords()
.GroupBy(s => new { s.Name, s.English })
.Select(s => new ClassData
{
English = s.Key.English,
Name = s.Key.Name
})
};
return PartialView(classesCustomViews);
This is the error that I get:
System.Data.SqlClient.SqlException: Invalid column name 'English'.
Invalid column name 'English'. Invalid column name 'English'.
Since you're not pulling back any aggregate info other than your keys, you should be able to do it without the grouping by just using Distinct assuming that you are working with IQueryable up the stack to the database:
var classesCustomViews = new Classes {
HomeWorkList = _repositoryClasses.GetRecords()
.Select(s => new ClassData
{
English = s.English,
Name = s.Name
})
.Distinct()
};
If you still get an error, I would check your mapping and table to make sure that the table column names didn't change.

Simplest way to flatten document to a view in RavenDB

Given the following classes:
public class Lookup
{
public string Code { get; set; }
public string Name { get; set; }
}
public class DocA
{
public string Id { get; set; }
public string Name { get; set; }
public Lookup Currency { get; set; }
}
public class ViewA // Simply a flattened version of the doc
{
public string Id { get; set; }
public string Name { get; set; }
public string CurrencyName { get; set; } // View just gets the name of the currency
}
I can create an index that allows client to query the view as follows:
public class A_View : AbstractIndexCreationTask<DocA, ViewA>
{
public A_View()
{
Map = docs => from doc in docs
select new ViewA
{
Id = doc.Id,
Name = doc.Name,
CurrencyName = doc.Currency.Name
};
Reduce = results => from result in results
group on new ViewA
{
Id = result.Id,
Name = result.Name,
CurrencyName = result.CurrencyName
} into g
select new ViewA
{
Id = g.Key.Id,
Name = g.Key.Name,
CurrencyName = g.Key.CurrencyName
};
}
}
This certainly works and produces the desired result of a view with the data transformed to the structure required at the client application. However, it is unworkably verbose, will be a maintenance nightmare and is probably fairly inefficient with all the redundant object construction.
Is there a simpler way of creating an index with the required structure (ViewA) given a collection of documents (DocA)?
FURTHER INFORMATION
The issue appears to be that in order to have the index hold the data in the transformed structure (ViewA), we have to do a Reduce. It appears that a Reduce must have both a GROUP ON and a SELECT in order to work as expected so the following are not valid:
INVALID REDUCE CLAUSE 1:
Reduce = results => from result in results
group on new ViewA
{
Id = result.Id,
Name = result.Name,
CurrencyName = result.CurrencyName
} into g
select g.Key;
This produces: System.InvalidOperationException: Variable initializer select must have a lambda expression with an object create expression
Clearly we need to have the 'select new'.
INVALID REDUCE CLAUSE 2:
Reduce = results => from result in results
select new ViewA
{
Id = result.Id,
Name = result.Name,
CurrencyName = result.CurrencyName
};
This prduces: System.InvalidCastException: Unable to cast object of type 'ICSharpCode.NRefactory.Ast.IdentifierExpression' to type 'ICSharpCode.NRefactory.Ast.InvocationExpression'.
Clearly, we also need to have the 'group on new'.
Thanks for any assistance you can provide.
(Note: removing the type (ViewA) from the constructor calls has no effect on the above)
UPDATE WITH CORRECT APPROACH
As outlined in Daniel's blog mentioned in the answer below, here is the correct way to do this for this example:
public class A_View : AbstractIndexCreationTask<DocA, ViewA>
{
public A_View()
{
Map = docs => from doc in docs
select new ViewA
{
Id = doc.Id,
Name = doc.Name,
CurrencyName = doc.Currency.Name
};
// Top-level properties on ViewA that match those on DocA
// do not need to be stored in the index.
Store(x => x.CurrencyName, FieldStorage.Yes);
}
}
One solution, simply flatten in the Map and configure the index to store only properties that do not exist in DocA.
public class A_View : AbstractIndexCreationTask<DocA, ViewA>
{
public A_View()
{
Map = docs => from doc in docs
select new ViewA
{
Id = doc.Id,
Name = doc.Name,
CurrencyName = doc.Currency.Name
};
// Top-level properties on ViewA that match those on DocA
// do not need to be stored in the index.
Store(x => x.CurrencyName, FieldStorage.Yes);
}
}

Categories