I have a file with comma separated values (CSV), that have this format:
26/09/2015,GROUP_1,0,0,0,0,0,0,0,0,0,0,12345.006,12345.006,27469.005,27469.005,27983.005,27983.005,28081.005,0,0,0,28105.005,28105.005,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Every number represents the work hours over an interval of 15 minutes, over the range 8:00 am - 8:00 pm. The first start time is 08:00:00) and the last start time will be 19:45:00; there are 49 "columns" of data.
0,0,0,0,0,0,0,0,0,0,12345.006,12345.006,27469.005,27469.005,27983.005,27983.005,28081.005,0,0,0,28105.005,28105.005,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
The date will be the date when the "event" happened and it's the date on the data. But I need to get the values that are the same and specify a time range. For example, those first two non-zero values are the same:
12345.006,12345.006
These start at 10:30 and 10:45; I need to merge these and report 12345 hours for the time span 10:30 - 11:00 am.
I read the file; I have those values as an array, and the problem I'm having is how to "group" the same values into the appropriate time ranges.
DateTime startDate = new DateTime(2015,08,05);
DateTime finisDahte = new DateTime(2015,08,05);
int column = 0;
for (int i = 0; i < data.Length; i++)
{
//timerange start with every 15 minutes by column
if (column >= 2)
{
if (data[i] != "0")
{
//Getting rid of decimals, they are not neccesary and that's how the file have it, I dont know why
if (data[i].Contains('.'))
{
data[i] = data[i].Substring(0, data[i].LastIndexOf('.'));
}
//we check if there is a next index to compare the same value
if ((i + 1) <= totalElementos)
{
var nextElem = data[i + 1];
if (nextElem != "0")
{
nextElem.Substring(0, nextElem.LastIndexOf('.'));
}
else
{
//the is no next element... something here
}
//CRUCIAL PART: if the current index it's the same as the next one, it means they share the time range
if (data[i] == nextElem)
{
//the same index as the next one
//I need to identify when it's the first time I'm comparing a value with the next one, so I can set a start date
//I need to sum the total amount of time ranges for every repetition they have and save when the value start and when the value is different (so it's a new value)
}
else
{
//it's not the same index, so technically the finishdate will be set here?
}
}
else
{
//there is not more indexes, so finishdate will be here
}
}
}
//column++;
}
Hope I could explain. Thanks
You need to generate a class like the code below. I changed the date to US format for testing. The code below reads from a string using StringReader and when reading from a file use StreamReader instead.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<DataSample> samples = new List<DataSample>();
string data = "9/26/2015,GROUP_1,0,0,0,0,0,0,0,0,0,0,12345.006,12345.006,27469.005,27469.005,27983.005,27983.005,28081.005," +
"0,0,0,28105.005,28105.005,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n";
StringReader reader = new StringReader(data);
string inputline = "";
while ((inputline = reader.ReadLine()) != null)
{
string[] dataArray = inputline.Split(new char[] { ',' });
DateTime startDate = DateTime.Parse(dataArray[0]);
startDate = startDate.AddHours(8);
DateTime timeCounter = startDate;
string groupName = dataArray[1];
for (int i = 2; i < dataArray.Length; i++)
{
if (dataArray[i] != "0")
{
DataSample newSample = new DataSample();
samples.Add(newSample);
newSample.name = groupName;
newSample.time = timeCounter;
newSample.value = double.Parse(dataArray[i]);
}
timeCounter = timeCounter.AddMinutes(15);
}
}
var groupByValue = samples.AsEnumerable()
.GroupBy(x => x.value)
.ToList();
foreach (var group in groupByValue)
{
Console.WriteLine("Value : {0}, Times : {1}", group.Key.ToString(), string.Join(",",group.Select(x => x.time.ToString())));
}
Console.ReadLine();
}
}
public class DataSample
{
public string name { get; set; }
public DateTime time { get; set; }
public double value { get; set; }
}
}
This is not a coding service; you need to go a little farther. You've done a good job of outlining your algorithm; now, you should put in a few print statements to track the operation of your code. Do the loops and if statements give you the control flow you expected? A good way to do this is to put your comments into print statements, such as in your last inside comment:
print "there is not more indexes, so finishdate will be here"
Also print the loop index, values you found, etc.
Once you have corrected any flow problems there, start filling in the comment-only blocks with useful code, a few lines or one block at a time.
When you hit a specific problem, post your code and the actual output. That's where StackOverflow is designed to help you.
In the meantime, I'd like to g a change to your outer loop. Let it be driven as a while loop, so you can freely advance your index as needed. Right now, you're keeping two variables for almost the same purpose: i and column. Instead, use just one, something like:
column = 0
while (column < data.Length)
// Find all of the indices with the same consecutive value
finish_index = column;
while data[start_column] = data[finish_index+1]
finish_index++;
// You now have the range of work slots to merge.
printf "Time slots %d - %d have %d work hours", column, finish_index, int(data[column])
column = finish_index+1
}
You will still have to convert column numbers to times: 15 minutes * column + 8:00am. I've also left out a few intermediate nice steps, but I think that you already have them in your comments.
Does this get you moving?
This code works properly well but it doesn't have a memory for storing user input.
I also need to categorize the grades into its respective columns in array format, having "S/N, Category and Count" and I dont know how to go about it. Any help would be appreciated.
namespace ExamScore
{
class YourExamScore
{
private static string GetGrade(int examScore)
{
if (examScore >= 90 && examScore <= 100)
return "Excellent";
if (examScore >= 70 && examScore <= 89)
return "Good";
if (examScore >= 50 && examScore <= 69)
return "Satisfactory";
if (examScore >= 0 && examScore <= 49)
return "Unsatisfactory";
return "Invalid";
}
static void Main(string[] args)
{
// Print a greeting message. After all, why not?
Console.WriteLine("Welcome to ExamScore Calculator!");
Console.WriteLine("Input Your Exam Score...");
Console.WriteLine("Press -2 when you have inputed all scores");
while (true)
{
var examScore = Convert.ToInt32(Console.ReadLine());
if (examScore == -2)
{
break;
}
var grade = GetGrade(examScore);
Console.WriteLine(grade);
}
Console.WriteLine("\n\nProccessing Scores... Please Wait...");
Console.ReadLine();
}
}
}
Well it seems like a homework, so i am not going to help with codes directly,
for the storage of marks, you can follow either of one way
if you know how many input you are going to use, then use a fixed size array
var marks = new int[10];
if the no. of input is not fixed then you can use List<int>
var marks = new List();
I am not sure if I understood you correctly, but I think you are asking to keep track of the amount of grades that fall in a certain category. In that case, I would do it as follows.
You will need to keep track of an array of ints (int[]) with an entry for each category that keeps track of the amount of grades in that category so far. So you will first have to make a static array of size 4, which will automatically initialize with zeroes everywhere. Semantically, each index stands for a category (say 0 for excellent, 1 for good, etc.). Then, in the GetGrade method you should for each category also increase the right category before returning.
To store user input in memory you can use array or list (List<MyData> is better if you have to insert often, Hashtable if you want to search fast, MyData[] - least memory and fastest access by index, etc.)
After entering -2 you may want to store (serialize) your data somehow, so data will persists between runs. It can be xml-file (XmlSerializer), own format text-/binary- file, database, etc.
You also need to define how you will work with data: are they saved immediately after entering (then database itself is a memory storage) or upon exit, delete/correct possibility and such.
Finally, you can add statistic and reporting, which query your data and produce results.
It is a bit unclear what
"S/N, Category and Count"
stands for. Could you elaborate? Your code is asking for score and producing grade. You could count how many users have which grade, how many total users, how many grades (all those are "Counts"), but "S/N" and "Category" are confusing.
To example, you only want to print how many users belongs different grades.
1) Very manual approach, count occurrences (statistic) upon entering (use)
int grade90plus; // fields
int grade70plus;
...
// inside GetGrade
if(examScore > 90)
{
grade90plus++;
return "Excellent";
}
else
if(examScore > 70)
{
grade70plus++;
return "Good";
}
...
// report
Console.WriteLine("Excellent: " + grade90plus);
Console.WriteLine("Good: " + grade90plus);
2) High tec (serialization unfriendly)
public class Grade
{
public string Name;
public int Count;
public int ScoreMin;
public int ScoreMax;
public bool Test(int score) { return score >= ScoreMin && score <= ScoreMax; }
public static readonly Grade Excellent = new Grade() {Name = "Excellent", ScoreMin = 90, ScoreMax = 100};
public static readonly Grade Good = new Grade() {Name = "Good", ScoreMin = 70, ScoreMax = 89};
public static readonly Grade[] GetAll = new Grade[] { Excellent, Good };
}
private static string GetGrade(int examScore)
{
foreach(var grade in Grade.GetAll)
if(grade.Test)
{
grade.Count++;
return grade.Name;
}
return "Invalid";
}
// report
foreach(var grade in Grade.GetAll)
Console.WriteLine(grade.Name + "," + grade.Count);
You can surely improve those or make own solution (serialization friendly, utilizing database, one what uses expressions for Test, etc).
I have been given a rather odd requirement to fulfill for a particular solution. The requirement is to write a function given the current number, to find the next consecutive number that excludes numbers with two or more consecutive 6s.
So far I have the following code (in C#) which I tested with a few inputs and it works. I know it's not the most efficient solution but it does the job, I just want to see if there's a more efficient way of doing this. The way I go about it is by converting the number into a string and using a simple regular expression to see if the next sequence is a valid one given the requirement. Also I am aware that it will throw a error once the number reaches its (2^31) - 1 limit, but at the moment that's not an issue.
public int GetNextSequenceNumber(int currentSequenceNumber)
{
var nextSequenceCandidate = currentSequenceNumber + 1;
var strNum = nextSequenceCandidate.ToString();
if (IsValidSequenceNumber(strNum))
{
return nextSequenceCandidate;
}
else
{
do
{
strNum = (++nextSequenceCandidate).ToString();
} while (!IsValidSequenceNumber(strNum));
return nextSequenceCandidate;
}
}
private bool IsValidSequenceNumber(string sequenceNumber)
{
return !Regex.IsMatch(sequenceNumber, "[6]{2,}");
}
I'm thinking there's another way where one would use division and the modulus operation to find out the digits position and increment that just as needed. Any input is appreciated, thanks!
The most efficient solution that I see would actually use string replacement, but this works only if you are incrementing and returning all values of the sequence. Just replace 66 by 67.
If any starting number is allowed, you will have to append just as many 0s as there were digits after the first occurrence of 66 in your number string.
Convert your number to decimal format, say to a byte array, scan for 66.
If you can't find it, your are done.
Other wise, change it to 67, followed by all zeros.
I think you best bet is to not think in terms of incrementing a number, but in terms of validating a string.
Something like the following would prevent potentially long loop runs, and should provide the next lowest possible value that doesn't have "66".
public static int GetNextSequenceNumber(int currentSequenceNumber)
{
int nextNumber = currentSequenceNumber += 1;
string nextNumberStr = nextNumber.ToString();
if (!nextNumberStr.Contains("66"))
{
return nextNumber;
}
else
{
//travel from left to right, find the 66, and increment the last 6 and reset the remaining values.
bool doreset = false;
bool lastwassix = false;
string newString = string.Empty;
for (int i = 0; i < nextNumberStr.Length; i++)
{
if (doreset) { newString += '0'; continue; }
char c = nextNumberStr[i];
if (c == '6')
{
if (lastwassix)
{
newString += '7';
doreset = true;
continue;
}
lastwassix = true;
}
newString += c;
}
return Convert.ToInt32(newString);
}
}
I am looking at having a option to specify how often something should run, in terms of a percentage.
So I would have a textbox, that takes a number 0-100 to detirmine a percentage. I want to then take this number, and convert in into a matching random statement.
So for example:
textPercentage.text = "10"
would be changed to something like:
if (rnd.Next(1, 11) = 1)
{
do();
}
What is the best way to convert that numerical value to a matching random statement?
var i = int.Parse(textPercentage.Text);
if (rnd.Next(1, 101) <= i)
{
do();
}
You might want to use int.TryParse in stead of int.Parse to gracefully handle non digit inputs.
First get your value back into an int.
In WPF, this is easy because you just databind to an int property.
In WinForms you use the int.Parse method to get the value back: int.Parse(textBox.Text)
If you use the values 0-100 you can use something similar to your original code, as-is.
int value = int.Parse(textBox.Text);
if(rnd.Next(0, 100) < value)
{
DoSomething();
}
Otherwise, just add one to both the start and end values:
int value = int.Parse(textBox.Text);
if(rnd.Next(1, 101) <= value)
{
DoSomething();
}
The first one is simpler, so I'd go with it :)