How to rewrite it more efficiently (linq with split)? - linq

here is the problem:
I have a column with the topics in the format: "boston | new york | chicago". The names can be different and number of the topics can vary in the records.
What I need to come up with is the collection of the same names with the count of those names.
This is what I have done:
var splitted = queryResult.Select(x => x.TopicData);
List<string> lstOfTopics = new List<string>();
foreach (var element in splitted)
{
string[] splitedTopics = element.Split('|');
lstOfTopics.AddRange(splitedTopics);
}
var groupedTopics = lstOfTopics.GroupBy(x => x).Select(group => new {key = group.Key, count = group.Count()}).AsEnumerable();
seems a lot of code for a simple task. any ideas how to simplify this?
thanks!

You can construct the list using SelectMany and ToList:
List<string> lstOfTopics = splitted.SelectMany(x => x.Split('|')).ToList();
You can also omit the construction of the list entirely:
var groupedTopics = queryResult
.SelectMany(x => x.TopicData.Split('|'))
.GroupBy(x => x)
.Select(group => new { group.Key, Count = group.Count()});

var groups = queryResult
.SelectMany(t => t.TopicData.Split('|')
.GroupBy(n => n)
.Select(g => new { Key = g.Key, Count = g.Count() });

query syntax can be nice and compact:
var topics =
from qr in queryResult
from topic in qr.TopicData.Split('|')
group topic by topic into g
select new { Topic = g.Key, Count = g.Count() };
If you want just the unique topics, but not their counts...
List<string> topicList = topics.Select(item => item.Topic);

Related

What's the equivalent of GROUP INTO in Linq extension method?

This is how I'm filtering and grouping transTasks.
var transTasks = from t in taskData
where t.RangeName == rName
group t by t.CultureID into g
select new { language = g.Key, tasks = g };
Now I've a new requirement. Depending on the conditions, I'may filter by RangeName or by TaskOrderId.
That's why I've transformed the above Linq code to the following;
var transTasks = taskData
.Where(predicate)
.GroupBy(???)
.Select(???);
I've researched but I can't still find the equivalent of group into for the extension method. I need to group those transTasks because there is a loop inside another loop.
Thanks for helping
GroupBy is the equivalent , and it seems you have figured it out, your query in Method Syntax would be:
var transTrasks = taskData.Where(t => t.RangeName == rName)
.GroupBy(t => t.CultureID)
.Select(g => new { language = g.Key, tasks = g });
As a side note, Any LINQ query in query expression compiles to Method Syntax.
var transTasks = taskData
.Where(predicate)
.GroupBy(t => t.CultureID)
.Select(g => new { language = g.Key, tasks = g });

Linq GroupBy to alter list

I have a list of items in an IList<>.Each listitem has a date and a few other fields.
I need to order the list by date and then change the list to only show a date for the first item and effectively set the date field to null for the other items if the date is repeated.
Example:
12/01/2012 500
12/01/2012 700
15/02/2012 900
15/02/2012 1100
27/05/2012 2000
Desired Result:
12/01/2012 500
null 700
15/02/2012 900
null 1100
27/05/2012 2000
Is this possible with the linq group by and order by?
Thanks
LINQ operators are not supposed to change the underlying data. You'd better use regular foreach if you're going to modify the data.
This should probably work:
var groups = items.GroupBy(x => x.Date).ToArray();
foreach (var group in groups)
{
foreach (var item in group.Skip(1)) item.Date = null;
}
I would avoid using such a construction since you'll have to double-check that GroupBy preserves order. Instead I would use something like this:
var sortedItems = items.OrderBy(x => x.Date);
var lastVisitedDate = (DateTime?) null;
foreach (var item in sortedItems)
if (Equals(item.Date, lastVisitedDate)) item.Date = null;
else lastVisitedDate = item.Date;
This should work:
var list = new List<DateItem>();
// Initialization ...
var dups = list.Select((Item,Index) => new{ Item,Index })
.GroupBy(x => x.Item.Date)
.Where(g => g.Count() > 1);
foreach(var dup in dups)
{
foreach (var nullable in dup.OrderBy(x => x.Item.Date).Skip(1))
{
list[nullable.Index].Date = null;
}
}
Assuming your class looks similar to this:
class DateItem {
public DateTime? Date;
public int OtherField;
}
Edit: Here's a working demo: http://ideone.com/cVL4G
One way is to use LINQ to get all of the followers and then set their dates to null in a loop:
// Use ToList() to make sortedItems non-lazy so it won't get ordered each time it's called.
var sortedItems = items.OrderBy(x => x.Date).ToList();
var followers = sortedItems.GroupBy(item => item.Date)
.SelectMany(group => group.Skip(1));
foreach (var follower in followers)
{
follower.Date = null;
}
// Now you can use sortedItems.
Or if you prefer the query syntax:
var followers = from item in sortedItems
group item by item.Date into grp
from follower in grp.Skip(1)
select follower;

Linq multi column grouping and averaging

I'm trying to get the average of some numbers after grouping but it just won't group for me. Why isn't this doing what I think it should?
var eventInfo = from eventData in dt.AsEnumerable()
group eventData by new
{
Phase1Minutes = eventData.Field<decimal>("Phase1Minutes"),
Phase2Minutes = eventData.Field<decimal>("Phase2Minutes"),
TechnologyType = eventData.Field<string>("TechnologyType"),
TechnologySubType = eventData.Field<string>("TechnologySubType")
} into g
select new
{
Phase1Avg = g.Average(x => x.Field<decimal>("Phase1Minutes")),
Phase2Avg = g.Average(x => x.Field<decimal>("Phase2Minutes")),
g.Key.TechnologyType,
g.Key.TechnologySubType
};
It makes no sense to average over what you're already grouping by. Just to give a trivial example, imagine grouping people by age, and then finding the average age within each group... they're all the same age!
My guess is you just need to take out the minutes parts of the grouping:
var eventInfo = from eventData in dt.AsEnumerable()
group eventData by new
{
TechnologyType = eventData.Field<string>("TechnologyType"),
TechnologySubType = eventData.Field<string>("TechnologySubType")
} into g
select new
{
Phase1Avg = g.Average(x => x.Field<decimal>("Phase1Minutes")),
Phase2Avg = g.Average(x => x.Field<decimal>("Phase2Minutes")),
g.Key.TechnologyType,
g.Key.TechnologySubType
};

LINQ: Group By + Where in clause

I'm trying to implement a T-SQL equivalent of a where in (select ...) code in LINQ.
This is what I have now:
int contactID = GetContactID();
IEnumerable<string> threadList = (from s in pdc.Messages
where s.ContactID == contactID
group 1 by new { s.ThreadID } into d
select new { ThreadID = d.Key.ThreadID}).ToList<string>();
var result = from s in pdc.Messages
where threadList.Contains(s.ThreadID)
group new { s } by new { s.ThreadID } into d
let maxMsgID = d.Where(x => x.s.ContactID != contactID).Max(x => x.s.MessageID)
select new {
LastMessage = d.Where(x => x.s.MessageID == maxMsgID).SingleOrDefault().s
};
However, my code won't compile due to this error for the ToList():
cannot convert from
'System.Linq.IQueryable<AnonymousType#1>'
to
'System.Collections.Generic.IEnumerable<string>'
Anyone have any suggestions on how to implement this? Or any suggestions on how to simplify this code?
Your query returns a set of anonymous types; you cannot implicitly convert it to a List<string>.
Instead, you should select the string itself. You don't need any anonymous types.
Change it to
var threadList = pdc.Messages.Where(s => s.ContactID == contactID)
.Select(s => s.ThreadID)
.Distinct()
.ToList();
var result = from s in pdc.Messages
where threadList.Contains(s.ThreadID)
group s by s.ThreadID into d
let maxMsgID = d.Where(x => x.ContactID != contactID).Max(x => x.MessageID)
select new {
LastMessage = d.Where(x => x.MessageID == maxMsgID).SingleOrDefault()
};

Need help with formulating LINQ query

I'm building a word anagram program that uses a database which contains one simple table:
Words
---------------------
varchar(15) alphagram
varchar(15) anagram
(other fields omitted for brevity)
An alphagram is the letters of a word arranged in alphabetical order. For example, the alphagram for OVERFLOW would be EFLOORVW. Every Alphagram in my database has one or more Anagrams. Here's a sample data dump of my table:
Alphagram Anagram
EINORST NORITES
EINORST OESTRIN
EINORST ORIENTS
EINORST STONIER
ADEINRT ANTIRED
ADEINRT DETRAIN
ADEINRT TRAINED
I'm trying to build a LINQ query that would return a list of Alphagrams along with their associated Anagrams. Is this possible?
UPDATE: Here's my solution based on the suggestions below! Thanks all!
using (LexiconEntities ctx = new LexiconEntities())
{
var words = ctx.words;
var query =
from word in words
where word.alphagram == "AEINRST"
group word by word.alphagram into alphagramGroup
select new { Alphagram = alphagramGroup.Key, Anagrams = alphagramGroup };
foreach (var alphagramGroup in query)
{
Console.WriteLine("Alphagram: {0}", alphagramGroup.Alphagram);
foreach (var anagram in alphagramGroup.Anagrams)
{
Console.WriteLine("Anagram: {0}", anagram.word1);
}
}
}
var list = anagrams.Select(
a => new {
Alphagram = a.ToCharArray().OrderBy(s => s).ToString(),
Anagram = a
}).toList();
A totally new answer...
You seem to need a groupby query look at How to: Group Data (Entity Framework).
this should accomplish what you want...
I did a testy with LINQ and this works...
var words = new List<Word>()
{
new ConsoleApplication1.Word("EINORST", "NORITES"),
new ConsoleApplication1.Word("EINORST", "OESTRIN"),
new ConsoleApplication1.Word("EINORST", "STONIER"),
new ConsoleApplication1.Word("ADEINRT", "ANTIRED"),
new ConsoleApplication1.Word("ADEINRT", "DETRAIN"),
new ConsoleApplication1.Word("ADEINRT", "TRAINED")
};
var q = words.GroupBy(w => w.Alphagram).Select(w => new { Alphagram = w.Key, Anagrams = w.Select(p => p.Anagram).ToList() }).ToList();
foreach (var item in q)
{
Console.WriteLine("Alphagram : {0}, Anagrams = {1}", item.Alphagram, String.Join(",", item.Anagrams));
}
var words = new List<Words>()
{
new Words("EINORST", "NORITES"),
new Words("EINORST", "OESTRIN"),
new Words("EINORST", "STONIER"),
new Words("ADEINRT", "ANTIRED"),
new Words("ADEINRT", "DETRAIN"),
new Words("ADEINRT", "TRAINED")
};
var result = words.GroupBy(w => w.Alphagram, w => w.Anagram)
.Select(w => new {
Alphagram = w.Key,
Anagrams = w.Where(p => w.Key.ToCharArray().SequenceEqualUnOrdered(p.ToCharArray())).ToList()
}
)
.ToList();
public static bool SequenceEqualUnOrdered<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
return new HashSet<T>(first).SetEquals(second);
}
Is it what you are looking for? It is LINQ to Objects. You may want to use LINQ-to-SQL or LINQ-to-Entites to fetch your records into your objects and then use the above-mentioned LINQ-to-Objects query over the already-fetched object collection.

Resources