How do I specify "not in" in this lambda expression? - c#

I have a quick question becuase my brain won't work with me...
Where do I specify that I want the user_id's in 'Users' that are NOT in 'Groups' ?
db.Users.Join(db.Groups, a => a.user_id, b => b.user_id, (a, b) => new SelectListItem
{
Value = a.user_id.ToString(),
Text = a.surname + " " + a.lastname
});

The following should work (assuming that I understood your question correctly):
db.Users
.Where(x => !db.Groups.Any(y => y.user_id == x.user_id))
.Select(a => new SelectListItem
{
Value = a.user_id.ToString(),
Text = a.surname + " " + a.lastname
});

you can try something like this:
var query = from u in db.Users
where !(from g in dc.Groups
select g.user_id)
.Contains(u.user_id)
select new SelectListItem {
Value = u.user_id.ToString(),
Text = u.surname + " " + u.lastname
};
Take a look here: http://introducinglinq.com/blogs/marcorusso/archive/2008/01/14/the-not-in-clause-in-linq-to-sql.aspx

Related

how to allow the user to choose how to sort his list using a variable?

the information that the user enters can be sorted in many ways ascending and descending and i'm trying to make the user choose how he want to see the data:
so is there any way to set a variable that the user enter and sort the data without repeating the code multiple time depending on the input like:
var empName = el.Select(i => new { i.ID, i.FullName });
if(emsort.Text="text1")
empName.OrderBy(i.text1);
else if...
by doing something shorter like:
string sort=emsort.Text ;
empName.OrderBy(sort);
If I understood your problem right, that you want to sort dynamically with the property name which is stored in emsort.Text, then you can use Expressions:
assuming empName is IEnumerable<Employee>, then use this:
private static Func<Employee, dynamic> GetSortable(string sortablePoperty)
{
var param = Expression.Parameter(typeof(Employee), "e");
var member = Expression.Property(param, sortablePoperty);
Expression memberAsObject = Expression.Convert(member, typeof(object));
return Expression.Lambda<Func<Employee, dynamic>>(memberAsObject, param).Compile();
}
then use it:
string sort=emsort.Text ;
empName.OrderBy(GetSortable(sort));
if empName is IQueryable<Employee>, then use this:
private static Expression<Func<Employee, dynamic>> GetSortable(string sortablePoperty)
{
var param = Expression.Parameter(typeof(Employee), "e");
var member = Expression.Property(param, sortablePoperty);
Expression memberAsObject = Expression.Convert(member, typeof(object));
return Expression.Lambda<Func<Employee, dynamic>>(memberAsObject, param);
}
If the User in your question is some software that calls your function, then the user is aware of the type of the objects in the el sequence: he knows which properties the elements in the el sequence are, and he knows how he wants them ordered.
Why not let this user give you the keySelector to use in the OrderBy?
Creating an extension function:
static class QueryableExtensions
{
IQueryable<MyType> SelectAndSort<TSource, TResult>(this IQueryable<TSource source,
Expression<Func<TSource, TResult>> selector,
Expression<Func<TSource, TKey>> sortKeySelector,
System.ComponentModel.ListSortDirection sortDirection)
{
var selectBeforeOrdering = myRepository.el.Select(selector);
// note: this is still an IQueryable, only the query has been made,
// the database is not accessed yet!
IQueryable<TResult> result = (sortDirection == ListSortDirectrion.Ascending) ?
// sort in ascending order:
selectBeforeOrdering.OrderBy(sortKeySelector) :
// else: selec in descending order:
selectBeforeOrdering.OrderByDescending(sortKeySelector);
return result;
}
}
Usage: suppose your user knows your el, and he want several fields of it, ordered by one of the properties of your el
using (var myDbContext = new DbContext(...))
{
IQueryable myEls = myDbContext.el;
var mySelectedItems = myEls.SelectAndSore(
// selector: what properties do I as a user want?
el => new
{
Id = el.Id,
FullName = el.FullName,
... // other desired properties
},
// sortSelector: how do I want my selected items to be sorted?
selectedItem => selectedItem.FullName,
// direction:
ListSortDirection.Descending);
// note: until now the database has not been accessed
// only the Expression of the query has been created.
// depending on what you want as a result: ToList / ToArray / First / ...
return mySelectedItems.ToList();
}
If, on the other hand your user is not software, but an operator, who has to select by which column he wants his items to be ordered, he has to have a method to tell the computer which column should be sorted.
This is usually done by clicking the column. Another method could be by selecting a value in a combo box. Anyway, you'll have to attach something to the column or to the combobox values that holds the sortKeySelector:
class MySortableColumn<TDisplayedProperty> : DataGridViewColumn
// or whatever column class you are using
{
public Expression<Func<MyDisplayedItem, TDisplayedProperty>> SortKeySelector{get;set;}
}
Now you have several columns that each display one of the properties of MyDisplayedItem:
var columnId = new MySortableColumn<int>()
{
SortKeySelector = displayedItem => myDisplayedItem.Id,
};
var columnFullName = new MyStortableColumn<string>()
{
SortKeySelector = displayedItem => myDisplayedItem.Id,
}
// etc.
Whenever the operator clicks on a column, the sortKey is extracted and used as parameter to sort the items:
void SortColumn(SortableColumn column)
{
var sortedItems = Sort(column.SortKeySelector, ListSortOrder...);
Display(sortedItems);
}
As I assume, el is materialized(i.e., for example, it is not just entry point to database), so you can use reflection:
var sort = emsort.Text;
PropertyInfo property = null;
var sortedData = el.Select(i => new { i.ID, i.FullName })
.OrderBy(x =>
{
property = property ?? x.GetType().GetProperty(sort);
return property.GetValue(x);
}).ToList();
this is a part of the code that i had to write to do it :
if (emord.Text == "Ascending")
{
if (emsort.Text == "ID")
{
var empName = el.Select(i => new { i.ID, i.FullName }).OrderBy(x => x.ID);
var empNamenAmp = el.Select(i => new { i.ID, i.FullName, i.Salary, i.Currency, i.Per }).OrderBy(x => x.ID);
var empNamenAmpnwh = el.Select(i => new { i.ID, i.FullName, i.Salary, i.Currency, i.Per, i.Hours }).OrderBy(x => x.ID);
var empNamenwh = el.Select(i => new { i.ID, i.FullName, i.Hours }).OrderBy(x => x.ID);
var empNamenbd = el.Select(i => new { i.ID, i.FullName, i.Date }).OrderBy(x => x.ID);
var empNamenad = el.Select(i => new { i.ID, i.FullName, i.Location }).OrderBy(x => x.ID);
var empNamenpn = el.Select(i => new { i.ID, i.FullName, i.PhoneNb }).OrderBy(x => x.ID);
var empNamena = el.Select(i => new { i.ID, i.FullName, i.Age }).OrderBy(x => x.ID);
if (empfilcomb.Text == "Name")
{
dispfil.Clear();
foreach (var x in empName)
dispfil.Text += x.ID + ", " + x.FullName + Environment.NewLine;
}
else if (empfilcomb.Text == "Name and Amount paid")
{
dispfil.Clear();
foreach (var x in empNamenAmp)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.Salary + " " + x.Currency + " " + x.Per + Environment.NewLine;
}
else if (empfilcomb.Text == "Name and Work Hours")
{
dispfil.Clear();
foreach (var x in empNamenwh)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.Hours + Environment.NewLine;
}
else if (empfilcomb.Text == "Name,Amount paid and Work Hours")
{
dispfil.Clear();
foreach (var x in empNamenAmpnwh)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.Salary + " " + x.Currency + " " + x.Per + ", " + x.Hours + Environment.NewLine;
}
else if (empfilcomb.Text == "Name and Birthday")
{
dispfil.Clear();
foreach (var x in empNamenbd)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.Date + Environment.NewLine;
}
else if (empfilcomb.Text == "Name and Address")
{
dispfil.Clear();
foreach (var x in empNamenad)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.Location + Environment.NewLine;
}
else if (empfilcomb.Text == "Name and Phone Number")
{
dispfil.Clear();
foreach (var x in empNamenpn)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.PhoneNb + Environment.NewLine;
}
else if (empfilcomb.Text == "Name and Age")
{
dispfil.Clear();
foreach (var x in empNamena)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.Age + Environment.NewLine;
}
}
else if (emsort.Text == "Name")
{
var empName = el.Select(i => new { i.ID, i.FullName }).OrderBy(x => x.FullName);
var empNamenAmp = el.Select(i => new { i.ID, i.FullName, i.Salary, i.Currency, i.Per }).OrderBy(x => x.FullName);
var empNamenAmpnwh = el.Select(i => new { i.ID, i.FullName, i.Salary, i.Currency, i.Per, i.Hours }).OrderBy(x => x.FullName);
var empNamenwh = el.Select(i => new { i.ID, i.FullName, i.Hours }).OrderBy(x => x.FullName);
var empNamenbd = el.Select(i => new { i.ID, i.FullName, i.Date }).OrderBy(x => x.FullName);
var empNamenad = el.Select(i => new { i.ID, i.FullName, i.Location }).OrderBy(x => x.FullName);
var empNamenpn = el.Select(i => new { i.ID, i.FullName, i.PhoneNb }).OrderBy(x => x.FullName);
var empNamena = el.Select(i => new { i.ID, i.FullName, i.Age }).OrderBy(x => x.FullName);
if (empfilcomb.Text == "Name")
{
dispfil.Clear();
foreach (var x in empName)
dispfil.Text += x.ID + ", " + x.FullName + Environment.NewLine;
}
else if (empfilcomb.Text == "Name and Amount paid")
{
dispfil.Clear();
foreach (var x in empNamenAmp)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.Salary + " " + x.Currency + " " + x.Per + Environment.NewLine;
}
else if (empfilcomb.Text == "Name and Work Hours")
{
dispfil.Clear();
foreach (var x in empNamenwh)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.Hours + Environment.NewLine;
}
else if (empfilcomb.Text == "Name,Amount paid and Work Hours")
{
dispfil.Clear();
foreach (var x in empNamenAmpnwh)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.Salary + " " + x.Currency + " " + x.Per + ", " + x.Hours + Environment.NewLine;
}
else if (empfilcomb.Text == "Name and Birthday")
{
dispfil.Clear();
foreach (var x in empNamenbd)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.Date + Environment.NewLine;
}
else if (empfilcomb.Text == "Name and Address")
{
dispfil.Clear();
foreach (var x in empNamenad)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.Location + Environment.NewLine;
}
else if (empfilcomb.Text == "Name and Phone Number")
{
dispfil.Clear();
foreach (var x in empNamenpn)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.PhoneNb + Environment.NewLine;
}
else if (empfilcomb.Text == "Name and Age")
{
dispfil.Clear();
foreach (var x in empNamena)
dispfil.Text += x.ID + ", " + x.FullName + ", " + x.Age + Environment.NewLine;
}
}
and i was asking if there is a shorter way to do it:withing what i'm allowed to use. Seems that there isn't but thanks a lot for the help i'm sure what you guys provided is useful.

LINQ left self join

I have LINQ query as below:
lst_direct_managers = context.sf_guard_user_profile
.Join(context.sf_guard_user_profile, up => up.user_id, dm => dm.direct_manager_id,
(up, dm) => new { up, dm })
.Where(m => m.up.is_gvo == 1)
.Select(m => new DirectManagerModel
{
user_id = m.up.direct_manager_id == null ? 0 : m.up.direct_manager_id,
dm_full_name = (m.up.first_name + " " + m.up.last_name == null ? "No Direct Manager" : m.up.first_name + " " + m.up.last_name)
})
.Distinct()
.OrderBy(m => m.dm_full_name).ToList();
Problem is that it does not return default value in case of nulls "No Direct Manager". Can you please help me?

Print distinct characters in a string with count

class Program
{
static void Main(string[] args)
{
string s = "Stack Overflows";
var x = from c in s.ToLower()
group c by c into a
select new { a.Key, Count = a.Count() };
Console.WriteLine(Convert.ToString(x));
Console.Read();
}
}
Output is system.linq.Enumable+
i want output like a 2 g 1 s 1 p 2 r 1
Console.WriteLine(String.Join(" ", x.Select(y=>y.Key + " " + y.Count)));
or using lambda syntax
string s = "Stack Overflows";
Console.WriteLine(String.Join(" ", s.GroupBy(c => c)
.Select(g => g.Key + " " + g.Count())));
You can also use aggregate function like this:
Console.WriteLine(x.Select(y => String.Format("{0} {1}", y.Key, y.Count)).Aggregate((y, z) => y + String.Format(" {0}", z)));
Aggregate function can be used for any types (not only strings)
Try this code instead of your code, I have modified #L.B code
string s = "Stack Overflows";
var x = String.Join("", (from c in s.ToLower()
group c by c into a
select new { a.Key, Count = a.Count() }).Select(y => y.Key + " " + y.Count));

ViewBag select list LINQ table join

Is it possible to join tables in a SelectList?
Here is what I have:
ViewBag.Clients = new SelectList(db.IPACS_Clients, "clientID", "name", 0);
I need to join the db.IPACS_Clients_Network table, I still need "clientID" but for "name" I need to be able to select IPACS_Clients.name + " - " + IPACS_Clients_Network.name
Is this possible with a Viewbag?
While this way is not recommended, I was able to find the answer.
ViewBag.Clients = new SelectList(db.IPACS_Clients_Network
.Join(
db.IPACS_Clients,
v => v.networkClientID,
s => s.networkClientID,
(v, s) =>
new
{
v = v,
s = s
}
)
.Select(
temp0 =>
new
{
v = temp0.v,
s = temp0.s
}
).Select(m => new SelectListItem
{
Value = SqlFunctions.StringConvert((double)m.s.clientID).Trim(),
Text = m.v.name + " - " + m.s.name
}), "Value", "Text", 0);

Complex LINQ query

I know LINQ but my knowledge is pretty much only selects, where, orderby and all of the most common functions. Now I have a need to do something that I think is really difficult and
maybe not even possible to do just with LINQ. What I have is a list of people. That's east
to query but I need to create a text string from that list. The text string has to give a letter followed by the name of each person.
IList<person> Person
I need to be able to have a LINQ statement that checks through the Person list. I need
to be able to look for names that appear more than once. So far I have the following. It works okay but doesn't give everything needed:
Person[0] name="Fred" &
Person[1] name="Pete" &
Person[2] name="Tony" the var abc = "a) Fred. b) Pete. c) Tony
var a = "";
foreach (var person in _persons
.Select((data, value) => new { Data = data, Value = value })
{
a = a + (char)(details.Value + 64) + details.name
}
What I need is the additional functionality so that:
Person[0] name="John" then var abc = "a) John."
Person[1] name="John" &
Person[3] name="John" then var abc = "b) & d) John."
Person[1] name="John" &
Person[2] name="John" &
Person[3] name="John" then var abc = "b),c) & d) John."
In other words, get the names and put a character before them that shows what position the name is in the list. However if the name appears twice then instead of a)name1. b)name1 I need to get a),b) name.
It's something I can't really figure out how to do. I would appreciate any advice or pointers that anyone can give me.
Given:
var persons=new[] {"Fred", "John", "John", "Pete", "John"};
You can write:
char id = 'a';
foreach (var row in persons
.Select(w => new { id = id++, name = w })
.GroupBy(w => w.name)
.Select(w => w
.Select(ww => ww.id + ")")
.Aggregate((c, n) => c + "&" + n)
+ " " + w.Key))
{
Console.WriteLine(row);
}
And that gives you:
a) Fred
b)&c)&e) John
d) Pete
I think I understand it, but I deserve a medal if I do.
foreach (var group in _persons
.Select((data, value) => new { Data = data, Value = value }
.GroupBy (x => x.Data))
{
foreach (var item in group)
a = a + (char)(item.Value + 64) + ") ";
a = a + group.Key;
}
What you need is to first group by the names, and then convert them to strings.
var result = names
.Select((name, index) => new { Name = name, Prefix = (char)(index + 'a') + ")" })
.GroupBy(p => p.Name, p => p.Prefix)
.Select(g => string.Join(" & ", g) + " " + g.Key);
This example doesn't entirely do the formatting of the a), b) & c) thing (instead it gives a) & b) & c), but it should get you started.
You can use Count() to achieve this. It can take a function to determine whether something should be counted.

Categories