I am building a website that does has a chat component to it. The code below receives from a stored procedure a list of messages with a lots of different paramaters. 1 of those is if a message is in reply to another, and if that is the case, duplicate the message that is being replied to over the message answer. If the message that was being replied to was also an answer to a previous message do the same ect. Now my issue is that I have not been able to figure out how to automate this part of the code without nesting if into one another until a point where I hope users won't reply in the same chain anymore.
To rephrase it, I go in a list in inverse order and check if the ReplyingTo is not null.
I then copy the row that has the same ID and ReplyingTo 1 row higher than the current row.
I then confirm that this new row has a ReplyingTo
If it does I copy that object 2 row higher than the current one.
and I would continue this way until I reached a certain point that the users would not reach.
If anyone got an idea on how to proceed I would be highly gracious. I have put an example of the type of data that would be given to this function below.
for (int i = publicChatCountList.Count-1 ; i > -1; i--)
{
if (publicChatCountList[i].ReplyingTo.HasValue)
{
Chat_Dto chatItem = new Chat_Dto();
long? ReplyingToId = publicChatCountList[i].ReplyingTo;
chatItem = publicChatCountList.Find(x => x.Id == ReplyingToId);
publicChatCountList.Insert(i+1, new Chat_Dto() {Text = chatItem.Text, IsPublic = chatItem.IsPublic, IsApproved = chatItem.IsApproved, ReplyingTo = chatItem.ReplyingTo });
publicChatCountList[i+1].Duplicate = true;
if (chatItem.ReplyingTo.HasValue)
{
Chat_Dto chatItem2 = new Chat_Dto();
long? ReplyingToId2 = chatItem.ReplyingTo;
chatItem2 = publicChatCountList.Find(x => x.Id == ReplyingToId2);
publicChatCountList.Insert(i + 2, new Chat_Dto() { Text = chatItem2.Text, IsPublic = chatItem2.IsPublic, IsApproved = chatItem2.IsApproved, ReplyingTo = chatItem2.ReplyingTo });
publicChatCountList[i + 2].Duplicate = true;
}
}
}
If I understood you correctly maybe running something like this to recursively get all replies would work:
private void Replies(Chat_Dto_List publicChatCountList,int i)
{
if (publicChatCountList[i].ReplyingTo.HasValue)
{
Chat_Dto chatItem = new Chat_Dto();
long? ReplyingToId = publicChatCountList[i].ReplyingTo;
chatItem = publicChatCountList.Find(x => x.Id == ReplyingToId);
publicChatCountList.Insert(i + 1, new Chat_Dto() { Text = chatItem.Text, IsPublic = chatItem.IsPublic, IsApproved = chatItem.IsApproved, ReplyingTo = chatItem.ReplyingTo });
publicChatCountList[i + 1].Duplicate = true;
if (chatItem.ReplyingTo.HasValue)
{
Replies(publicChatCountList, publicChatCountList.FindIndex(x => x.Id == chatItem.ReplyingTo))
}
}
}
I'm working in C# (.Net 4) and I am trying to do several things:
I have 2 files ("Offline.csv","online.csv"), and I'm having those files make one "master" file (called "Attendance.csv")
Both offline.csv and online.csv contain similar data---
My Offline.csv file has:
(ID),(TimeInMin),(DateWithoutSlashes yyymmdd)
01,10,20151201
01,05,20151202
02,11,20151201
03,11,20151202
My Online.csv file has
(ID),(TimeInMin),(DateWithoutSlashes yyymmdd)
01,70,20151201
02,20,20151202
03,22,20151202
After my program is ran, the Attendance.csv should look something like:
(Same headers)
01,80,20151201
01,05,20121502 (notice the date from offline.csv, which doesn't exist in the online.csv)
02,31,20151201
03,33,20151202
So what I'm trying to do is:
Compare the data from both the offline.csv and online.csv files. If data matches on the "ID" and "Date" columns, add the minutes together (column 2) and put them as a row in the Attendance.csv file
However, IF the offline.csv contains rows that the online.csv doesn't have, then put all those other records into the Attendance.csv on their own. Perform the same action with the online.csv, being mindful to not duplicate minutes that were already merged together from step #1
I don't know if that all makes sense, but I hope it does :X
I have been beating my head against the wall all day with this, and I don't know what else to look at.
With all that said, here is what I have so far:
I have created my own class, called "aoitime", it looks as follows:
public class aoitime
{
public string ID { get; set; }
public string online { get; set; }
public string offline { get; set; }
public string dtonline { get; set; }
public string dtoffline { get; set; }
public string date { get; set; }
}
I then use IEnumerable in a different function, looks similar to ...
IEnumerable<aoitime> together =
from online in onlinefile
let onlineFields = online.Split(',')
from id in offlinefile
let offlineFields = id.Split(',')
where (onlineFields[0] == offlineFields[0] && onlineFields[2] == offlineFields[2]) || (!offlineFields[1].Contains(""))
orderby onlineFields[0]
select new aoitime
{
ID = onlineFields[0],
online = onlineFields[1],
offline = offlineFields[1],
dtonline = onlineFields[2],
dtoffline = offlineFields[2],
date = onlineFields[2]
};
StreamWriter Attendance = new StreamWriter(destination);
Attendance.Write("SIS_NUMBER,MINUTES,DATE" + Environment.NewLine);
foreach (aoitime att in together)
{
int date = int.Parse(att.date);
int dateonline = int.Parse(att.dtonline);
int dateoffline = int.Parse(att.dtoffline);
int online = int.Parse(att.online);
int offline = int.Parse(att.offline);
int total = (online + offline);
Console.WriteLine("Writing total time now: "+online);
Attendance.Write(att.ID + "," + total + "," date + Environment.NewLine);
}
I then tried creating another IEnumerable class spawn that looks similar to the one above, but instead using "where offlineFields[2] != onlineFields[2]" but I get unpredictable results. I just don't know where else to look or what else to do.
Please be gentle, I'm very much new to programming in general (I promise this isn't for a classroom assignment :-)
thanks so much for any advice and reading this book!
You are almost there. I wrote this code, so hopefully you will be able to learn something from it.
First you only need one entity class for this. Note the ToString method. You will see how it's used later.
public class Attendance
{
public int Id { get; set; }
public int TimeInMinutes { get; set; }
public string Date { get; set; }
public override string ToString()
{
return string.Format("{0},{1},{2}", Id, TimeInMinutes, Date);
}
}
Now the code to parse your files and create the new file. Read my comments in the code.
var onlineEntries = File.ReadAllLines(#"c:\online.txt");//read online file
var validOnlineEntries = onlineEntries.Where(l => !l.Contains("(")); //remove first line
var onlineRecords = validOnlineEntries.Select(r => new Attendance()
{
Id = int.Parse(r.Split(new[] {","}, StringSplitOptions.None)[0]),
TimeInMinutes = int.Parse(r.Split(new[] {","}, StringSplitOptions.None)[1]),
Date = r.Split(new[] {","}, StringSplitOptions.None)[2],
}).ToList();//popultae Attendance class
var offlineEntries = File.ReadAllLines(#"c:\offline.txt"); //read online file
var validOfflineEntries = offlineEntries.Where(l => !l.Contains("(")); //remove first line
var offlineRecords = validOfflineEntries.Select(r => new Attendance()
{
Id = int.Parse(r.Split(new[] { "," }, StringSplitOptions.None)[0]),
TimeInMinutes = int.Parse(r.Split(new[] { "," }, StringSplitOptions.None)[1]),
Date = r.Split(new[] { "," }, StringSplitOptions.None)[2],
}).ToList();//popultae Attendance class
var commonRecords = (from n in onlineRecords
join f in offlineRecords on new {n.Date, n.Id } equals new {f.Date, f.Id} //if Date and Id are equal
select new { n.Id, TimeInMinutes = (n.TimeInMinutes + f.TimeInMinutes), n.Date }).OrderBy(x => x.Id).Distinct().ToList(); //add Online and Off line time
var newRecords = commonRecords.Select(r => new Attendance()
{
Id = r.Id,
TimeInMinutes = r.TimeInMinutes,
Date = r.Date,
}); //Poulate attendance again. So we can call toString method
onlineRecords.AddRange(offlineRecords); //merge online and offline
var recs = onlineRecords.Distinct().Where(r => !newRecords.Any(o => o.Date == r.Date && o.Id == r.Id)).ToList(); //remove already added items from merged online and offline collection
newRecords.AddRange(recs);//add filtered merged collection to new records
newRecords = newRecords.OrderBy(r => r.Id).ToList();//order new records by id
File.WriteAllLines(#"C:\newFile.txt", newRecords.Select(l => l.ToString()).ToList()); //write new file.
Just to add this as an answer, I am selecting #Kosala-w's suggestion as an answer. My code now looks very identical to what he posted, except I modified the ID to a string format because the integers used for the IDs are pretty lenghty.
I thank both people who answered this question, and I appreciate the SO community! Have a good day :-)
public class Attendance
{
public string Id { get; set; }
public int TimeInMinutes { get; set; }
public int Code { get; set; }
public string Date { get; set; }
public override string ToString()
{
return string.Format("{0},{1},{2}", Id, TimeInMinutes, Date);
}
}
I also have more rows that I have to handle in the Attendance sheet than I stated in my original question (I didn't worry about those because I wasn't concerned that I'd have a hard time getting what I needed.)
Anyway, the code below is what I used, again, thanks Kosala.
private void createAttendance()
{
try
{
txtStatus.ResetText();
txtStatus.Text += "Creating Attendance file. Please wait.";
string destination = (#"C:\asdf\Attendance.csv");
barStatus.Caption = "Processing Attendance file. Please wait.";
if (File.Exists(destination))
File.Delete(destination);
var validOnlineEntries = File.ReadAllLines(#"C:\asdf\online.csv");//read online file
//var validOnlineEntries = onlineEntries.Where(l => !l.Contains("(")); //remove first line
var onlineRecords = validOnlineEntries.Select(r => new Attendance()
{
Id = (r.Split(new[] { "," }, StringSplitOptions.None)[0] + ",202" + "," + txtYear.Text),
TimeInMinutes = int.Parse(r.Split(new[] { "," }, StringSplitOptions.None)[1]),
Date = r.Split(new[] { "," }, StringSplitOptions.None)[2],
}).ToList();//populate Attendance class
var validOfflineEntries = File.ReadAllLines(#"C:\asdf\offline.csv"); //read online file
//var validOfflineEntries = offlineEntries.Where(l => !l.Contains("(")); //remove first line
var offlineRecords = validOfflineEntries.Select(r => new Attendance()
{
Id = (r.Split(new[] { "," }, StringSplitOptions.None)[0] + ",202" + "," + txtYear.Text),
TimeInMinutes = int.Parse(r.Split(new[] { "," }, StringSplitOptions.None)[1]),
Date = r.Split(new[] { "," }, StringSplitOptions.None)[2],
}).ToList();//populate Attendance class
var commonRecords = (from n in onlineRecords
join f in offlineRecords on new { n.Date, n.Id } equals new { f.Date, f.Id } //if Date and Id are equal
select new { n.Id, TimeInMinutes = (n.TimeInMinutes + f.TimeInMinutes), n.Date }).OrderBy(x => x.Id).Distinct().ToList(); //add Online and Off line time
var newRecords = commonRecords.Select(r => new Attendance()
{
Id = r.Id,
TimeInMinutes = r.TimeInMinutes,
Date = r.Date,
}).ToList(); //Populate attendance again. So we can call toString method
onlineRecords.AddRange(offlineRecords); //merge online and offline
var recs = onlineRecords.Distinct().Where(r => !newRecords.Any(o => o.Date == r.Date && o.Id == r.Id)).ToList(); //remove already added items from merged online and offline collection
newRecords.AddRange(recs);//add filtered merged collection to new records
newRecords = newRecords.OrderBy(r => r.Id).ToList();//order new records by id
StreamWriter Attendance = new StreamWriter(destination);
//Attendance.Write("SIS_NUMBER,SCHOOL_CODE,SCHOOL_YEAR,ABSENCE_DATE,ABSENCE_REASON1,ABSENCE_REASON2,MINUTES_ATTEND,NOTE,ABS_FTE1,ABS_FTE2" + Environment.NewLine);
Attendance.Write("SIS_NUMBER,SCHOOL_CODE,SCHOOL_YEAR,MINUTES_ATTEND,ABSENCE_DATE,ABSENCE_REASON2,ABSENCE_REASON1,NOTE,ABS_FTE1,ABS_FTE2" + Environment.NewLine);
Attendance.Dispose();
File.AppendAllLines(destination, newRecords.Select(l => l.ToString()).ToList()); //write new file.
Convert_CSV_To_Excel();
}
catch(Exception ex)
{
barStatus.Caption = ("ERROR: "+ex.Message.ToString());
}
}
I plan to do some more fine tuning, but this sure got me in the right direction!
The first thing that I'd do is define a simpler class to hold your aoitimes. For example:
public class aoitime
{
public string ID { get; set; }
public int TimeInMinutes { get; set; }
public string DateWithoutSlashes { get; set; }
}
Then, you'll want to parse the string from the csv file into that class. I figure that that's an implementation detail that you can probably figure out on your own. If not, leave a comment and I can post more details.
Next, the tricky part is that you want not only a join, but you want the exceptions as well. The join logic is fairly simple:
var matches = from offline in offlineItems
join online in onlineItems
on
new {offline.ID, offline.DateWithoutSlashes} equals
new {online.ID, online.DateWithoutSlashes}
select new aoitime
{
ID = offline.ID,
TimeInMinutes = offline.TimeInMinutes + online.TimeInMinutes,
DateWithoutSlashes = offline.DateWithoutSlashes
};
(Notice there that you're using anonymous objects in the "ON" join condition). But the hard part is how to get the exceptions. LINQ is set up to do inner joins or equijoins, but I'm not sure about outer joins. At least I haven't seen it.
So one solution might be to use the LINQ join to get the matches and then another LINQ query to get those that don't match and then combine those two collections and write them out to a file.
Another solution might be to go back to basics and do the iteration logic yourself. LINQ is just elegant iteration logic and if it doesn't do what you need it to do, you might need to do it yourself.
For example, let's say that you have your collection of online and offline items and you want to iterate through them and do the comparison:
List<aoitime> offlineItems = <some method that produces this list>
List<aoitime> onlineItems = <some method that produces this list>
List<aoitime> attendanceItems = new List<aoitime>();
//For simplicity, assuming that you have the same number of elements in each list
for (int i = 0; i < offlineItems.Count; i++)
{
aoitime offline = offlineItems[i];
aoitime online = onlineItems[i];
if(offline.ID == online.ID && offline.DateWithoutSlashes = online.DateWithoutSlashes)
{
//Create your new object and add it to the attendance items collection.
}
else
{
//Process the exceptions and add them individually to the attendance items collection.
}
}
So you do the iteration and processing yourself and have control over the whole process. Does that make sense? If not, let me know in a comment and I can add more.
i need copy and paste some records of my table with just but change one field.
here is my code:
using (ClearWhiteDBEntities cwContext = new ClearWhiteDBEntities())
{
var qlstfld = from lstflds in cwContext.tblListFields
where lstflds.listId == theLongSrc
select lstflds;
foreach (var item in qlstfld)
{
tblListField lstFldRow = new tblListField
{
name = item.name,
filterFieldId = item.filterFieldId,
listId = theLongDes, //this field must be change in paste
continueById = item.continueById,
destinationId = item.destinationId,
conditionId = item.conditionId,
userId = userId,
date = Convert.ToDateTime(DateTime.Now.ToShortDateString()),
time = DateTime.Now.TimeOfDay,
IP = trueIp
};
cwContext.AddTotblListFields(lstFldRow);
cwContext.SaveChanges();
}
}
but i get this error:
an error accured white starting a transaction on the provider connection. see the inner exception for details.
what is best solution to copy and paste records but change one field?
If you are using change tracking:
using (ClearWhiteDBEntities cwContext = new ClearWhiteDBEntities())
{
var qlstfld = from lstflds in cwContext.tblListFields
where lstflds.listId == theLongSrc
select lstflds;
foreach (var item in qlstfld)
{
cwContext.ObjectStateManager.ChangeObjectState(item, System.Data.EntityState.Added);
item.Id = 0;
item.listId = theLongDes; //this field must be change in paste
}
cwContext.SaveChanges();
}
I am trying to change date to all workouts in the list, the other properties are working fine, its only when I change Date i get problem. I have tried different trouble shooting, but none have worked. I have tried with using updatedWorkout.Date as workoutStart = out of range. If I use old.Date, then, how can I add the new date with 7days a part ?
Maybe there is a better way to do this?
Here's my method:
private int UpdateForAllWorkouts(IWorkoutCommand updatedWorkout)
{
try
{ // get schedule
var schedule = _scheduleRepository.GetIncludeWorkouts(updatedWorkout.ScheduleId);
// get workouts within the schedule by schedule id
var workout = schedule.Workouts.FirstOrDefault(w => w.Id == updatedWorkout.Id);
for (var workoutStart = workout.Date; workoutStart <= schedule.ToDate; workoutStart = workoutStart.AddDays(7))
{
// get specdfic workout by workout id and start time
var old = schedule.Workouts.Single(w => w.Date == workoutStart && w.StartTime == workout.StartTime);
var tmp = new Model.Workout
{
Id = old.Id,
CourseId = updatedWorkout.CourseId,
InstructorId = updatedWorkout.InstructorId,
Date = //<---problem
StartTime = updatedWorkout.StartTime,
EndTime = updatedWorkout.EndTime,
ScheduleId = updatedWorkout.ScheduleId,
WeekOffSet = updatedWorkout.WeekOffSet.Days
};
}
return updatedWorkout.Id;
}
catch (Exception ex)
{
throw new Exception("");
}
}
thx for helping!
I think you can use a loop for 7.
like this :
var workoutStart = workout.Date;
while(true){
if(workoutStart <= schedule.ToDate){
// Do something
}else{
break;
}
workoutStart = workoutStart.AddDays(7);
}
Please consider to check the value of our DateTime properties.Their values could be less than your SQL server allow for datetime field.
What is the Date value of updatedWorkOut object before you assign "tmp" object ?
Is your "old" object null or what is the value of date ?
your code seems to be ok. the problem underlies the value of DateTime properties and fields.