I wanted to ask if there is a better way to achieve what I'm trying to do.
I need to get all the records of a child graph that have the date greter or equal to the given date, and the first record with date minor to the given date.
I found this solution that works but I'm not sure that this will be the best way.
var q = context.Istc0.Include("Interests").Where(a => a.IIsin == listKey).Select(a => new
{
Istc0 = a,
Interests = a.Interests.Where(d => d.InDat >= date)
});
var qq = context.Istc0.Include("Interests").Where(a => a.IIsin == listKey).Select(a => new
{
Istc0 = a,
Interests = a.Interests.Where(d => d.InDat < date).OrderByDescending(d => d.InDat).Take(1)
});
var xxx = q.ToList()[0].Istc0;
xxx = qq.ToList()[0].Istc0;
return xxx;
I do not know which one you need to return. Probably yyy.
var q = context.Istc0.Include("Interests").Where(a => a.IIsin == listKey).Select(a => new
{
Istc0 = a,
Interests = a.Interests.Where(d => d.InDat >= date)
}).ToList();
var xxx = q[0].Istc0;
var yyy = q.OrderByDescending(d => d.InDat).Take(1).SingleOrDefault().Istc0;
Dictionary<string,decimal> result = new Dictionary<string,decimal>();
result.Add("all",xxx);
result.Add("previous",yyy);
return result;
In this example i create a Dictionary with a string key (could be an integer or whatever you like) and with a decimal(i am guessing the returned value is of decimal type) value to store the results of the queries. Then i return this dictionary.
Another way would be to have a strongly typed object and return a list of that object.
Lastly, you could define two output parameters (read this). For instance:
public void GetInterestRates(string listKey, out decimal currentRate, out decimal previousRate)
{
var q = context.Istc0.Include("Interests").Where(a => a.IIsin == listKey).Select(a => new
{
Istc0 = a,
Interests = a.Interests.Where(d => d.InDat >= date)
}).ToList();
var currentRate = q[0].Istc0;
var previousRate = q.OrderByDescending(d => d.InDat).Take(1).SingleOrDefault().Istc0;
}
And when you want to use this:
decimal currentRate , previousRate;
GetInterestRates(listKey, currentRate , previousRate);
Hi thanks for the answer.
With the query you posted I don't get all the data I need. I don't think that I can get what I need with one query.
Interest table
-----------------
2013-5-16 | 1%
2013-6-21 | 0.8%
2013-7-12 | 0.5%
2013-8-06 | 0.6%
The istc0 table contains all my isin number, and as a relationship with the interest table One to Many.
On of the parameter of the function is the date. so if I pass for example
date = 2013-7-01
the result I need is the following:
Interest table
-----------------
2013-6-21 | 0.8%
2013-7-12 | 0.5%
2013-8-06 | 0.6%
So in my forst post the first query assigned to q retrieves all the interests after july the first, the second query assigned to qq retrieves the first interest rate before july the first.
My function does the job, and I get the set of data that I need, but it doesn't seem to be the cleanest way to do it.
Related
Suppose I have this table:
Image
Perimeter
a
1
b
1
b
2
d
3
e
1
I want to return the images that have relationship with only ONE perimeter.
The expected result would be images "a,d,e" because image "b" has relationship with perimeter "1" and "2".
The objective is to remove the releated image when I delete the perimeter. But if it is linked to another perimeter, I can't remove it.
How can I write this query with LINQ?
I think it would be something like this:
SELECT "ImageId"
WHERE "PerimeterId" = PerimeterId IN
(
SELECT "ImageId"
GROUP BY "ImageId"
HAVING COUNT("PerimeterId") = 1
)
but I don't know how to convert it to LINQ.
You could use a NOT EXISTS
var query = dbo.Table
.Where(t => !dbo.Table.Any(t2 => t.Image = t.Image && t.Perimeter != t2.Perimeter));
You can easily adapt this to only select the image part. But, if you are coming from SQL, thinking about "Selecting rows" based on a "HAVING()" group calculation, then you will want to look at the .SelectMany() LINQ method. This lets you "combine back together data partitioned into groups". While your needs are to only return "one from each group", it's easy to see where this can be adjusted.
This can be run in the "C# interactive window" of SSDT 2015:
struct imagePerimeter { //this might be whatever object type it is for you...
public string Image { get; set; } //a,b,b,d,e
public int Perimeter { get; set; } //1,1,2,3,1
}
Func<string, int, imagePerimeter> newIP = (i, p) => new imagePerimeter() { Image = i, Perimeter = p };
List<imagePerimeter> results = new List<imagePerimeter>() { {newIP("a",1) }
,{newIP("b",1) }
,{newIP("b",2) }
,{newIP("d",3) }
,{newIP("e",1) } };
Func<imagePerimeter, string> ipImage = (ip) => ip.Image; //the Func's "ipImage" and "newIP" could just be inlined into LINQ, but it helps to see and debug at times IMO.
var imagesWithOnePerimeter = results.GroupBy<imagePerimeter, string>(ipImage) //even in SQL, the "GROUP BY" conceptually comes first, in LINQ, it comes first in code too!
.Select(grp => new { Image = grp.Key, PerimeterCount = grp.Count(), Details = grp }) //there's probably a more technical term, but notice how we "carry forward" the original reference to [grp]
.Where(subTotals => subTotals.PerimeterCount == 1)
.SelectMany(filtered => filtered.Details.AsEnumerable())
.ToList();
Is any way to select all fields in a query but modifying one field like this?
var notes = from n in myContext.Notes
select new
{
... // all fiedls
date = n.date.ToString("MM/YYYY") // but one field edited
}
this is the query I want but less verbose when I have several properties.
var note = await _dbContext.ClientChartNotes
.Select(s => new
{
s.ClientChartNoteId,
s.ClientId,
s.ChartNoteType,
s.Title,
s.Note,
ChartNoteDate = s.ChartNoteDate.ToString("MM/dd/yyyy")
})
.FirstOrDefaultAsync(s => s.ClientChartNoteId.Equals(id));
Lambda preferred.
Thanks
Edit: to include original query.
I recommend selecting the Note itself and the additional field.
var notes = from n in myContext.Notes
select new
{
Note= n
NewDate = n.date.ToString("MM/YYYY")
}
So your notes will have all the Original Note and the additional properties you added in the result.
Your query using lambda:
var fetchedNote = await myDbContext.Notes // get the collection of all Notes
.Where(note => note.ClientChartNoteId == Id) // take only those notes that ...
.Select(note => new // from every remaining note, make one new object
{ // with only the properties you plan to use
Title = note.Title, // some are original values
...
Date = note.Data.ToString(...), // some are calculated values
})
.FirstOrDefaultAsync();
I have this need to know how many rows have the same month from a table and I have no idea of how to do it. I thought I'd try some LINQ but I've never used it before so I don't even know if it's possible. Please help me out!
public ActionResult returTest()
{
ViewData["RowsWithSameMonth"] = // I'm guessing I can put some LINQ here?
var returer = from s in db2.ReturerDB select s;
return View(returer.ToList());
}
The ideal would be to get, maybe a two dimensional array with the month in the first cell and the amount of rows from the db in the second?
I'd like the result to be sort of :
string[,] statistics = new string[,]
{
{"2013-11", "5"},
{"2013-12", "10"},
{"2014-01", "3"}
};
Is this doable? Or should I just query the database and do a whole lot of stuff? I'm thinking that I can solve this on my own, but it would mean a lot of ugly code. Background: self taught C# developer at IT-company with 1 years experience of ugly codesmanship and no official degree of any kind.
EDIT
var returer = from s in db2.ReturerDB select s;
var dateRange = returer.ToList();
var groupedData = dateRange.GroupBy(dateRow => dateRow.ToString())
.OrderBy(monthGroup => monthGroup.Key)
.Select(monthGroup => new
{
Month = monthGroup.Key,
MountCount = monthGroup.Count()
});
string test01 = "";
string test02 = "";
foreach (var item in groupedData)
{
test01 = item.Month.ToString();
test02 = item.MountCount.ToString();
}
In debug, test01 is "Namespace.Models.ReturerDB" and test02 is "6" as was expected, or at least wanted. What am I doing wrong?
You can do this:
var groupedData = db2.ReturerDB.GroupBy(r => new { r.Date.Year, r.Date.Month })
.Select(g => new { g.Key.Year, g.Key.Month, Count = g.Count() })
.OrderBy(x => x.Year).ThenBy(x => x.Month);
.ToList();
var result = groupedData
.ToDictionary(g => string.Format("{0}-{1:00}", g.Year, g.Month),
g => g.Count);
Which will give you
Key Value
---------------
2013-11 5
2013-12 10
2014-01 3
(Creating a dictionary is slightly easier than a two-dimensional array)
This will work against a SQL back-end like entity framework of linq-to-sql, because the expressions r.Date.Year and r.Date.Month can be translated into SQL.
with a nod to mehrandvd, here is how you'd achieve this using linq method chain approach:
var dateRange = { // your base collection with the dates};
// make sure you change MyDateField to match your won datetime field
var groupedData = dateRange
.GroupBy(dateRow => dateRow.MyDateField.ToString("yyyy-mm"))
.OrderBy(monthGroup => monthGroup.Key)
.Select(monthGroup => new
{
Month = monthGroup.Key,
MountCount = monthGroup.Count()
});
This would give you the results you required, as per the OP.
[edit] - as requested, example of how to access the newly created anonymous type:
foreach (var item in groupedData)
{
Console.WriteLine(item.Month);
Console.WriteLine(item.MountCount);
}
OR, you could return the whole caboodle as a jsonresult to your client app and iterate inside that, i.e the final line of your view would be:
return Json(groupedData, JsonRequestBehavior.AllowGet);
hope this clarifies.
What you need is grouping.
Considering you have a list of dates a solution would be this:
var dateRows = // Get from database
var monthlyRows = from dateRow in dateRows
group dateRow by dateRow.ToString("yyyy/mm") into monthGroup
orderby monthGroup.Key
select new { Month=monthGroup.Key, MountCount=monthGroup.Count };
// Your results would be a list of objects which have `Month` and `MonthCount` properties.
// {Month="2014/01", MonthCount=24}
// {Month="2014/02", MonthCount=28}
I have a list of anonymous types that I get from my database:
var takenChannels = (from b in bq.GetStuff(db)
where b.RecordType == "H" && b.TourStartDateTime.Date == date
select new { Start = b.TourStartDateTime, End = b.TourEndDateTime, Channel = b.RadioChannel, TourArea = b.TourArea }).ToList();
Then I use this list info to do some stuff in a foreach loop. I want to add to this list a new anonymous item for when I come back round in the loop.
Something like:
takenChannels.Union{new[] { new{Start = DateTime.Now, End = DateTime.Now.AddDays(1), Channel = 25, TourArea = "Area" }});
Obviously this doesn't work. How do I do it?
Edit 1:
takenChannels.Add(new { Start = s, End = e, Channel = channel, TourArea = booking.TourArea });
This is the closest I've got so far (Thanks to Daniel)... but the error I get is:
Error 6 Argument 1: cannot convert from 'AnonymousType#2' to 'AnonymousType#1'
This answer might be a bit late, but since this is the question I found when Googling for the same problem, I think I should complete it with a working answer.
There is no problem to Union multiple times over anonymous types. It is important that all properties are declared in all instances and that they have the same data type. if not, you get the error above.
In your specific case, does the database perhaps return TourStartDateTime or TourEndDateTime as DateTime??
Is RadioChannel an int from the database or perhaps an int? or string?
Is TourArea a string in the database?
Just make sure the data types match and you should be fine. Below is a working snippet of code I use in my own program:
var regions = (
new[] { new { Id = "-1", Name = "---", Pattern = (string)null } }
).Union(
from x in db.Userlists where x.ListType == 2 select new { Id = x.UserlistID.ToString(), Name = x.Name, Pattern = (string)null }
).Union(
from x in db.Lookups where x.Category == "Stock" select new { Id = x.Key, Name = x.Key, Pattern = x.Value }
).ToArray();
You can simply Add to the list:
takenChannels.Add(new { Start = ... });
I'm trying to select a subgroup of a list where items have contiguous dates, e.g.
ID StaffID Title ActivityDate
-- ------- ----------------- ------------
1 41 Meeting with John 03/06/2010
2 41 Meeting with John 08/06/2010
3 41 Meeting Continues 09/06/2010
4 41 Meeting Continues 10/06/2010
5 41 Meeting with Kay 14/06/2010
6 41 Meeting Continues 15/06/2010
I'm using a pivot point each time, so take the example pivot item as 3, I'd like to get the following resulting contiguous events around the pivot:
ID StaffID Title ActivityDate
-- ------- ----------------- ------------
2 41 Meeting with John 08/06/2010
3 41 Meeting Continues 09/06/2010
4 41 Meeting Continues 10/06/2010
My current implementation is a laborious "walk" into the past, then into the future, to build the list:
var activity = // item number 3: Meeting Continues (09/06/2010)
var orderedEvents = activities.OrderBy(a => a.ActivityDate).ToArray();
// Walk into the past until a gap is found
var preceedingEvents = orderedEvents.TakeWhile(a => a.ID != activity.ID);
DateTime dayBefore;
var previousEvent = activity;
while (previousEvent != null)
{
dayBefore = previousEvent.ActivityDate.AddDays(-1).Date;
previousEvent = preceedingEvents.TakeWhile(a => a.ID != previousEvent.ID).LastOrDefault();
if (previousEvent != null)
{
if (previousEvent.ActivityDate.Date == dayBefore)
relatedActivities.Insert(0, previousEvent);
else
previousEvent = null;
}
}
// Walk into the future until a gap is found
var followingEvents = orderedEvents.SkipWhile(a => a.ID != activity.ID);
DateTime dayAfter;
var nextEvent = activity;
while (nextEvent != null)
{
dayAfter = nextEvent.ActivityDate.AddDays(1).Date;
nextEvent = followingEvents.SkipWhile(a => a.ID != nextEvent.ID).Skip(1).FirstOrDefault();
if (nextEvent != null)
{
if (nextEvent.ActivityDate.Date == dayAfter)
relatedActivities.Add(nextEvent);
else
nextEvent = null;
}
}
The list relatedActivities should then contain the contiguous events, in order.
Is there a better way (maybe using LINQ) for this?
I had an idea of using .Aggregate() but couldn't think how to get the aggregate to break out when it finds a gap in the sequence.
Here's an implementation:
public static IEnumerable<IGrouping<int, T>> GroupByContiguous(
this IEnumerable<T> source,
Func<T, int> keySelector
)
{
int keyGroup = Int32.MinValue;
int currentGroupValue = Int32.MinValue;
return source
.Select(t => new {obj = t, key = keySelector(t))
.OrderBy(x => x.key)
.GroupBy(x => {
if (currentGroupValue + 1 < x.key)
{
keyGroup = x.key;
}
currentGroupValue = x.key;
return keyGroup;
}, x => x.obj);
}
You can either convert the dates to ints by means of subtraction, or imagine a DateTime version (easily).
In this case I think that a standard foreach loop is probably more readable than a LINQ query:
var relatedActivities = new List<TActivity>();
bool found = false;
foreach (var item in activities.OrderBy(a => a.ActivityDate))
{
int count = relatedActivities.Count;
if ((count > 0) && (relatedActivities[count - 1].ActivityDate.Date.AddDays(1) != item.ActivityDate.Date))
{
if (found)
break;
relatedActivities.Clear();
}
relatedActivities.Add(item);
if (item.ID == activity.ID)
found = true;
}
if (!found)
relatedActivities.Clear();
For what it's worth, here's a roughly equivalent -- and far less readable -- LINQ query:
var relatedActivities = activities
.OrderBy(x => x.ActivityDate)
.Aggregate
(
new { List = new List<TActivity>(), Found = false, ShortCircuit = false },
(a, x) =>
{
if (a.ShortCircuit)
return a;
int count = a.List.Count;
if ((count > 0) && (a.List[count - 1].ActivityDate.Date.AddDays(1) != x.ActivityDate.Date))
{
if (a.Found)
return new { a.List, a.Found, ShortCircuit = true };
a.List.Clear();
}
a.List.Add(x);
return new { a.List, Found = a.Found || (x.ID == activity.ID), a.ShortCircuit };
},
a => a.Found ? a.List : new List<TActivity>()
);
Somehow, I don't think LINQ was truly meant to be used for bidirectional-one-dimensional-depth-first-searches, but I constructed a working LINQ using Aggregate. For this example I'm going to use a List instead of an array. Also, I'm going to use Activity to refer to whatever class you are storing the data in. Replace it with whatever is appropriate for your code.
Before we even start, we need a small function to handle something. List.Add(T) returns null, but we want to be able to accumulate in a list and return the new list for this aggregate function. So all you need is a simple function like the following.
private List<T> ListWithAdd<T>(List<T> src, T obj)
{
src.Add(obj);
return src;
}
First, we get the sorted list of all activities, and then initialize the list of related activities. This initial list will contain the target activity only, to start.
List<Activity> orderedEvents = activities.OrderBy(a => a.ActivityDate).ToList();
List<Activity> relatedActivities = new List<Activity>();
relatedActivities.Add(activity);
We have to break this into two lists, the past and the future just like you currently do it.
We'll start with the past, the construction should look mostly familiar. Then we'll aggregate all of it into relatedActivities. This uses the ListWithAdd function we wrote earlier. You could condense it into one line and skip declaring previousEvents as its own variable, but I kept it separate for this example.
var previousEvents = orderedEvents.TakeWhile(a => a.ID != activity.ID).Reverse();
relatedActivities = previousEvents.Aggregate<Activity, List<Activity>>(relatedActivities, (items, prevItem) => items.OrderBy(a => a.ActivityDate).First().ActivityDate.Subtract(prevItem.ActivityDate).Days.Equals(1) ? ListWithAdd(items, prevItem) : items).ToList();
Next, we'll build the following events in a similar fashion, and likewise aggregate it.
var nextEvents = orderedEvents.SkipWhile(a => a.ID != activity.ID);
relatedActivities = nextEvents.Aggregate<Activity, List<Activity>>(relatedActivities, (items, nextItem) => nextItem.ActivityDate.Subtract(items.OrderBy(a => a.ActivityDate).Last().ActivityDate).Days.Equals(1) ? ListWithAdd(items, nextItem) : items).ToList();
You can properly sort the result afterwards, as now relatedActivities should contain all activities with no gaps. It won't immediately break when it hits the first gap, no, but I don't think you can literally break out of a LINQ. So it instead just ignores anything which it finds past a gap.
Note that this example code only operates on the actual difference in time. Your example output seems to imply that you need some other comparison factors, but this should be enough to get you started. Just add the necessary logic to the date subtraction comparison in both entries.