Lets say I have an array like this:
string [] Filelist = ...
I want to create an Linq result where each entry has it's position in the array like this:
var list = from f in Filelist
select new { Index = (something), Filename = f};
Index to be 0 for the 1st item, 1 for the 2nd, etc.
What should I use for the expression Index= ?
Don't use a query expression. Use the overload of Select which passes you an index:
var list = FileList.Select((file, index) => new { Index=index, Filename=file });
string[] values = { "a", "b", "c" };
int i = 0;
var t = (from v in values
select new { Index = i++, Value = v}).ToList();
You cannot get an index using pure LINQ query expressions (those with from.. where.. select.. clauses).
However, this doesn't mean you have to completely give up on this LINQ query style.
You just have to get out of the LINQ query expression and use a .Select(item, index) method overload.
var newestExistingFilesWithIndexes =
(from f in Filelist
// we love LINQ query expressions
where f.Exists
// and we use it anywhere possible
orderby f.LastModified descending
select f)
// but sometimes we have to get out and use LINQ extension methods
.Select((f, index) => new { Index = index, Filename = f.Fullname});
or suppose, you need to filter a list based on item index ...
var newestExistingFilesOnlyEvenIndexes =
// use the Select method overload to get the index
(from f in Filelist.Select((file, index) => new { file, index })
// only take item with an even index
where f.index % 2 == 0
where f.file.Exists
orderby f.file.LastModified descending
select f.file);
Related
I'm trying to convert a SQL expression to Linq but I can't make it work, does anyone help?
SELECT
COUNT(descricaoFamiliaNovo) as quantidades
FROM VeiculoComSeminovo
group by descricaoFamiliaNovo
I try this:
ViewBag.familiasCount = db.VeiculoComSeminovo.GroupBy(a => a.descricaoFamiliaNovo).Count();
I need to know how many times each value repeats, but this way it shows me how many distinct values there are in the column.
You can try:
var list = from a in db.VeiculoComSeminovo
group a by a.descricaoFamiliaNovo into g
select new ViewBag{
familiasCount=g.Count()
};
or
var list = db.VeiculoComSeminovo.GroupBy(a => a.descricaoFamiliaNovo)
.Select (g => new ViewBag
{
familiasCount=g.Count()
});
If you need column value:
new ViewBag{
FieldName=g.Key,
familiasCount=g.Count()
};
You don't need the GROUP BY unless there are fields other than the one in COUNT. Try
SELECT
COUNT(descricaoFamiliaNovo) as quantidades
FROM VeiculoComSeminovo
UPDATE, from your comment:
SELECT
COUNT(descricaoFamiliaNovo) as quantidades,
descricaoFamiliaNovo
FROM VeiculoComSeminovo
GROUP BY descricaoFamiliaNovo
That's it as SQL. In LINQ it is something like:
var reponse = db.VeiculoComSeminovo.GroupBy(a => a.descricaoFamiliaNovo)
.Select ( n => new
{Name = n.key,
Count = n.Count()
}
)
Not tested.
Ty all for the help.
I solved the problem using this lines:
// get the objects on db
var list = db.VeiculoComSeminovo.ToList();
// lists to recive data
List<int> totaisFamilia = new List<int>();
List<int> totaisFamiliaComSN = new List<int>();
// loop to cycle through objects and add the values I need to their lists
foreach (var item in ViewBag.familias)
{
totaisFamilia.Add(list.Count(a => a.descricaoFamiliaNovo == item && a.valorSeminovo == null));
totaisFamiliaComSN.Add(list.Count(a => a.descricaoFamiliaNovo == item && a.valorSeminovo != null));
}
The query was a little slow than I expected, but I got the data
Say I have a list of all Projects, and that I group them by Category like this:
var projectsByCat = from p in Projects
group p by p.Category into g
orderby g.Count() descending
select new { Category = g.Key, Projects = g };
Now I want to display this as a list in a web page, where first I create the left side div, secondly the right side div. I am ordering by number of Projects in each Category to show the Categories with the most Projects on top - thus I would like to split projectsByCat in two - if I put all the "odd numbered" Categories on the left and the "even numbered" categories on the right, I think I will get a reasonably sane view.
So I thought I could do this to get the odd and even members of projectsByCat:
var oddCategories = projectsByCat.Where((cat, index) => index % 2 != 0);
var evenCategories = projectsByCat.Where((cat, index) => index % 2 == 0);
And it compiles - however, when I run it, I get an exception such as this:
Unsupported overload used for query operator 'Where'.
And I thought I was safe since it compiled in the first place.. ;)
Is there an elegant way to do this? And also, is there an elegant explanation for why my creative use of Where() won't work?
If you're using LINQ to SQL or LINQ to Entities you should first fully materialize the results into memory:
var oddCategories = projectsByCat.ToList().Where((c,i) => i % 2 != 0);
var evenCategories = projectsByCat.ToList().Where((c,i) => i % 2 == 0);
It isn't possible to iterate through results on the database with an indexer without the use of a cursor, which either ORM framework does not do.
Note that calling .ToList() twice for the same query is going query the database twice.
It would be much better to cache the result in an intermediate list, then apply your predicate filtering:
var projectsByCat =
(from p in Projects
group p by p.Category into g
orderby g.Count() descending
select new { Category = g.Key, Projects = g }).ToList();
var oddCategories = projectsByCat.Where((cat, index) => index % 2 != 0);
var evenCategories = projectsByCat.Where((cat, index) => index % 2 == 0);
The oddCategories and the evenCategories are backward.
Indexes start a 0 not 1
0 % 2 = 0
0 index is odd.
var oddCategories = projectsByCat.Where((cat, index) => index % 2 == 0);
var evenCategories = projectsByCat.Where((cat, index) => index % 2 != 0);
The proper way to do this using LINQ, and avoiding multiple enumerations over the input, is to do a grouping or similar on whether each item is even or odd.
A simple way using the overload for Select that mixes in an index coupled with ToLookup gives you what you want:
var oddsAndEvens = input
.ToList() // if necessary to get from IQueryable to IEnumerable
.Select((item, index) => new { isEven = index % 2 == 0, item })
.ToLookup(
i => i.isEven,
i => i.item);
This will produce a Lookup<TKey, TElement> data structure that has the following benefit:
If the key is not found in the collection, an empty sequence is returned.
This means that after the above LINQ query you can do:
var evens = oddsAndEvens[true];
var odds = oddsAndEvens[false];
You can separate odd and even in your view using linq.
//even
#foreach (var item in Model.Where((item, index) => index % 2 == 0))
{
//do the code
}
//odd
#foreach (var item in Model.Where((item, index) => index % 2 != 0))
{
//do the code
}
You Can find Even odd number without foreach loop
static void Main(string[] args)
{
List<int> lstnum = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> lstresult = lstnum.FindAll(x => (x % 2) == 0);
lstresult.ForEach(x => Console.WriteLine(x));
}
var text = "this is a test <string> to extract odd <index> values after split";
var parts = text.Split(new char[] { '<', '>' });
IEnumerable words = parts.Where(x => parts.ToList().IndexOf(x) % 2 == 1)
words would contain "string" and "index"
Using Linq GroupBy Method:
List<string> lista = new List<string> { "uno", "dos", "tres", "cuatro" };
var grupoXindices = lista.GroupBy(i => (lista.IndexOf(i) % 2) == 0);
foreach (var grupo in grupoXindices)
{
Console.WriteLine(grupo.Key);
foreach (var i in grupo) Console.WriteLine(i);
}
Need help with linq query. I have a column which contains alphanumeric data in it and I want to extract numbers and perform mathematical comparisons. For example >,< etc.
So the data would be like: JS20, MRR12, DEEN2 etc.
I tried converting it to Int but it fails
var TheData = (from p in db.data.Where(l => Convert.ToInt16(l.TextAndNumber) <= 10)
group p by p.Names into g
select g.Key).ToList();
return Json(new { data = TheData });
Try something like this:
(from row in db.data
let digits = new String(row.TextAndNumber.Where(Char.IsDigit).ToArray())
let number = Int64.Parse(digits)
where number < 10
select row.Names
).Distinct();
You need to extract the numbers from the string before applying your conversion.
List<string> data = new List<string> { "JS20", "MRR112", "DEEN2" };
var TheData = data.Where(d => Convert.ToInt32(new string(d.Where(char.IsNumber).ToArray())) <= 10);
This returns DEEN2.
Is there a way to write a query in LINQ to return the count of the search terms found in a field(s)
Basically, I want this to work:
var matches = from t in _db.Books
let score = GetScore(t, searchterms)
where score >= 1
orderby score descending
select t;
public static int GetScore(Book b, params string[] searchterms)
{
int count = 0;
foreach (string term in searchterms)
{
if (b.Title.Contains(term))
count++;
}
return count;
}
But, of course, that can't work.
Can my little GetScore function be translated into LINQ?
Thanks.
EDIT: I would also prefer to have the score accessible. Ideally I will be selecting my results into a SearchResults class (for the View) that would contain some Book info and the Book's score from the query. To update my query, it'd be something like this:
var matches = from t in _db.Books
let score = GetScore(t, searchterms)
where score >= 1
orderby score descending
select new SearchResult
{
Title = t.Title,
Type = "Book",
Link = "Books/Details/" + t.BookID,
Score = score
};
I'm sorry I wasn't more clear originally.
You can't do what you want to do without issuing multiple queries to the database - essentially one per search term. If you are happy to do that, then here is an easy way to do it:
var terms = new [] { "s", "t", "r", "e", "b", "c", };
var ids =
from term in terms
from id in _db.Books
.Where(book => book.Title.Contains(term))
.Select(book => book.Id)
group term by id into gts
orderby gts.Count() descending
select gts.Key;
var selectedIds = ids.Take(50).ToArray();
var query =
from book in _db.Books
where selectedIds.Contains(book.Id)
select book;
I wrote the ids to return a list of ids sorted by those that match the most terms first. This was to most closely get the same kind of result that you wanted in your question. I then decided to use a Take(50) to get the top 50 results. You can obviously change this strategy to suit your needs, but you must end up with an array of ids to use in the final query.
I hope this helps.
EDIT: based on OP's edit.
Here's how to query with the score included:
var terms = new [] { "s", "t", "r", "e", "b", "c", "l", "i", };
var idScores =
from term in terms
from id in _db.Books
.Where(book => book.Title.Contains(term))
.Select(book => book.BookID)
group term by id into gts
select new
{
Id = gts.Key,
Score = gts.Count(),
};
var selectedIds = idScores.Select(x => x.Id).Take(50).ToArray();
var selectedBooks =
from book in _db.Books
where selectedIds.Contains(book.BookID)
select book;
var query =
from b in selectedBooks.ToArray()
join x in idScores on b.BookID equals x.Id
orderby x.Score descending
select new
{
Title = b.Title,
Type = "Book",
Link = "Books/Details/" + b.BookID,
Score = x.Score,
};
If you want to convert your GetScore() function in LINQ then your can change whole LINQ to this:
var matches = from t in _db.Books
where searchterms.Count(c => c == t.Title) >= 1
orderby searchterms.Count(c => c == t.Title)
select t;
Now it will compile successfully but on run time when you will bind this matches to grid or any where it will throw exception "Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator."
Because "problem is that we are trying to join an SQL table and an in-
memory list. Since you write the query against the SQL table, it goes
through LINQ to SQL, which rightly complains that it cannot do that.
so If you really want to do an in-memory join, then you must use _db.Books.AsEnumerable()
then query will be:
var matches = from t in _db.Books.AsEnumerable()
where searchterms.Count(c => c == t.Title) >= 1
orderby searchterms.Count(c => c == t.Title)
select t;
You have two options:
Do the search in the DB via a Stored procedure that returns a pair (BookId, Score), and then use that to do the query in LINQ2SQL
Use ToList() to execute the query, and avoid getting the "Local sequence cannot be used..." error.
For the second option, the query (in lambda syntax) would be something like
db.Books
.ToList()
.Select(t=> new SearchResult {
Title = t.Title,
Type = "Book",
Link = "Books/Details/" + t.BookID,
Score = GetScore(t, searchTerms)
})
.Where(t => t.Score >=1);
In the latter case, you'll be bringing the whole Book table to memory (and using LINQ2Objects to do the filtering), so I'd rather go for the first one.
if you want to get filtered data tou may build Expression by your self. I make some examle for your task, but you need to improve it for your source, or create some global improvements
let's start from the end. I can't make Expression returned anonymous Type so I create Generic wich I am planning to use as result
public class GRes<T>
{
public T Key { get; set; }
public int Count { get; set; }
}
I'm going to make grouping by any field of object or the object itself, and then on this grouping results call Sum fnction with some lambda which look's like this
x=>0 + IIF(x.Code.Contains(p1), 1, 0) + IIF(x.Code.Contains(p2), 1, 0) ...
where Code - is some field and p1,p2... - your search terms
at last call would be
IQueriable<GRes<Book>> result = context.Books
.GroupBy(d => d).CountIn(searchTerms, "Code")
.Where(r => r.Count > 0)
.OrderByDescending(r => r.Count);
CountIn - is extention :
public static IQueryable<GRes<TKey>> CountIn<TKey, TValue>(this IQueryable<IGrouping<TKey, TValue>> source, IEnumerable<string> values, Expression<Func<TValue,string>> selector)
{
ParameterExpression xExpr = selector.Parameters[0];
Expression propExpr = selector.Body;
MethodInfo mi = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
Expression res = Expression.Constant(0);
foreach (string term in values)
{
Expression value = Expression.Constant(term);
MethodCallExpression methodEpr = Expression.Call(propExpr, mi,value);
Expression tx = Expression.Condition(methodEpr, Expression.Constant(1), Expression.Constant(0));
res = Expression.Add(res, tx);
}
var r0 = Expression.Lambda<Func<pp_Disease, int>>(res, xExpr);
Type groupingType = typeof(IGrouping<TKey, TValue>);
ParameterExpression selPar = Expression.Parameter(groupingType, "i");
MethodInfo mi1 = typeof(Enumerable).GetMethods()
.FirstOrDefault(m => m.Name == "Sum"
&& m.ReturnParameter.ParameterType == typeof(int)
&& m.GetParameters().Count() == 2)
.MakeGenericMethod(typeof(pp_Disease));
Expression r1 = Expression.MemberInit(Expression.New(typeof(GRes<TKey>))
, Expression.Bind(typeof(GRes<TKey>).GetMember("Count")[0], Expression.Call(mi1, selPar, r0))
, Expression.Bind(typeof(GRes<TKey>).GetMember("Key")[0], Expression.Property(selPar, "Key")));
return source.Select(Expression.Lambda<Func<IGrouping<TKey, TValue>, GRes<TKey>>>(r1, selPar));
}
and when this function would be called your get SQL like this:
SELECT
...
FROM ( SELECT
...
FROM ( SELECT
....
SUM([Extent1].[A1]) AS [A1]
FROM ( SELECT
...
0 + (CASE WHEN ([Extent1].[Code] LIKE N'%2%') THEN 1 ELSE 0 END) + (CASE WHEN ([Extent1].[Code] LIKE N'%I%') THEN 1 ELSE 0 END) AS [A1]
FROM (SELECT
...
FROM [dbo].[pp_Disease] AS [pp_Disease]) AS [Extent1]
) AS [Extent1]
GROUP BY [K1], [K2], [K3], [K4]
) AS [GroupBy1]
WHERE [GroupBy1].[A1] > 0
) AS [Project1]
ORDER BY [Project1].[C1] DESC
i removed some fields declarations (EF generated sql huge), most important for this example is case string with parameters that we put in our function
i took your problem as below
books.Add("Robin HOOD");
books.Add("Charles");
books.Add("James");
search.Add("Rob");
search.Add("ood");
search.Add("les");
search.Add("am");
so you want the count of search items which are found in any of the books so if run this example , you will get the correct result.
var temp = searchterms.Where(x=>books.Where(y=>y.Title.Contains(x)).Count()>0);
var matches = from t in _db.Books
let score = searchterms.Where(term => t.Title.Contains(term)).Count()
where score >= 1
orderby score descending
select t;
edit
if its linq to sql i think my solution is devide into 2 part
// just results in db
string terms = searchterms.Aggregate((cur,nex) => cur+"^"+nex);
var results = from t in _db.Books
where terms.Contains(t.Title.Contains)
select t;
// sort results in c#
var sorting = for entry in results
let score = searchterms.Where(term => entry.Title.Contains(term)).Count()
orderby score
select new {......};
edit
oooh sorry little mistake. on first query
string terms = searchterms.Aggregate((cur,nex) => cur+"^"+nex);
var results = from t in _db.Books
where terms.Contains(t.Title.Contains)
select t;
change to
string terms = searchterms.Aggregate((cur,nex) => cur+"^"+nex);
var results = from t in _db.Books
where terms.Contains("^" + t.Title + "^")
select t;
the reason i use this query is 'var results' will gives all the result from db which should be ok becus it's string.Contains. Then sort the result in next query.
I've been using 101 LINQ Samples to get my feet wet using LINQ. It's been a good first resource, but I can't see an example there of what I currently need.
I just need to associate a sequential group number with each group. I have a working solution:
var groups =
from c in list
group c by c.Name into details
select new { Name = details.Key, DetailRecords = details };
int groupNumber = 0;
foreach (var group in groups)
{
//
// process each group and it's records ...
//
groupNumber++;
}
But, I'm sure it's possible to use LINQ to also generate the groupNumber. How?
This depends on your exact needs, but you can use:
var groupArray = groups.ToArray();
Similarly, you can use ToList. These data structures are sequential, and each group has an index.
If you do need the index on the object you create, another option is to use Select:
list.GroupBy(c => c.Name)
.Select((details, ind) =>
new
{
Name = details.Key,
DetailRecords = details,
Index = ind
});
this should do the trick:
int groupNumber = 0;
var groups =
from c in list
group c by c.Name into details
select new { Name = details.Key, DetailRecords = details, grpNum = groupNumber++};
if it's just a sequential group number, just use the Count() method on your IEnumerable.
var groups =
from c in list
group c by c.Name into details
select new {Name = details.Key, DetailRecords = details};
for(int i = 0; i < groups.Count(); i++)
{
//Process Records
}
Then, if you need the specific group number, you can just grab i.