Reputation: 15
So this is my code so far. Its a mess I know, any ideas about how I can make it work?
I want to output how many times a number appears inside a textfile. I want to get the numbers from a specific line inside the code that starts with the letter Time
Count the total number of members across all 3 time slots
The text file is like this:
*****************************
Participant: 1
Location: UK
Name: George
Phone Number: 69347653633
Time Slot: 1
*****************************
*****************************
Participant: 2
Location: FR
Name: Alex
Phone Number: 69635343623
Time Slot: 2
*****************************
*****************************
Participant: 3
Location: gr
Name: Maria
Phone Number: 694785896
Time Slot: 3
*****************************
For example, I want an output like this:
Total Member Registered for Slot 1: 5
Total Member Registered for Slot 2: 1
Total Member Registered for Slot 3: 3
The numbers are in a range of 1 to 3
The Output that i get so far is:
1 was found 1 times
1 was found 1 times
1 was found 1 times
2 was found 1 times
3 was found 1 times
1 was found 1 times
1 was found 1 times
1 was found 1 times
1 was found 1 times
1 was found 1 times
Any ideas about how I can improve it and fix it?
public static void ReadNumbers()
{
// Declare list
string[] lines = File.ReadAllLines("text.TXT");
IEnumerable<string> selectLines = lines.Where(line => line.StartsWith("Time"));
foreach (var item in selectLines)
{
var getNumbers = (from num in item where char.IsNumber(num) select num).ToArray();
//Console.WriteLine(new string(getNumbers));
getNumbers.ToArray();
foreach (var group in getNumbers.GroupBy(n => n))
{
Console.WriteLine("{0} was found {1} times", group.Key, group.Count());
}
}
}
Upvotes: 1
Views: 262
Reputation: 8004
Here's an alternative solution using a different approach, not the best, but an alternative. A few notes:
IEnumerable<Participant>
with all their properties; you now can use them when ever.You can create a new class that would represent your data (notes in code):
public class Participant
{
public IEnumerable<Participant> Participants {get; set;}
public int ParticipantNumber { get; set; }
public string ParticipantName { get; set; }
public string ParticipantLocation { get; set; }
public long ParticipantPhoneNumber { get; set; }
public int ParticipantTimeSlot { get; set; }
public void GetParticipants()
{
IEnumerable<string> lines = null;
using (OpenFileDialog ofd = new OpenFileDialog())
if (ofd.ShowDialog() == DialogResult.OK)
// remove empty lines and lines that start with *
lines = File.ReadLines(ofd.FileName).Where(line => !string.IsNullOrEmpty(line) && !line.StartsWith("***"));
// if we don't have anything return
if (lines == null)
return;
// get all our participants we can based on just 5 fields
Participants = lines.Select((value, index) => new { value, index})
.GroupBy(grp => grp.index / 5, myVal => myVal.value)
.Select(val => new Participant()
{
ParticipantNumber = int.TryParse(val.Select(s => s).Where(s=> s.StartsWith("Participant:"))
.FirstOrDefault().Replace("Participant:", string.Empty).Trim(), out int parNumber) ? parNumber : 0,
ParticipantLocation = val.Select(s => s).Where(s => s.StartsWith("Location:"))
.FirstOrDefault().Replace("Location:", string.Empty).Trim(),
ParticipantName = val.Select(s => s).Where(s => s.StartsWith("Name:"))
.FirstOrDefault().Replace("Name:", string.Empty).Trim(),
ParticipantPhoneNumber = long.TryParse(val.Select(s => s).Where(s => s.StartsWith("Phone Number:"))
.FirstOrDefault().Replace("Phone Number:", string.Empty).Trim(), out long parPhone) ? parPhone : 0,
ParticipantTimeSlot = int.TryParse(val.Select(s => s).Where(s => s.StartsWith("Time Slot:"))
.FirstOrDefault().Replace("Time Slot:", string.Empty).Trim(), out int parTime) ? parTime : 0
}) ;
}
}
public static class LinqExtentions
{
// Extension method by: Chris St Clair
public static IEnumerable<IEnumerable<T>> GroupWhile<T>(this IEnumerable<T> seq, Func<T, T, bool> condition)
{
T prev = seq.First();
List<T> list = new List<T>() { prev };
foreach (T item in seq.Skip(1))
{
if (condition(prev, item) == false)
{
yield return list;
list = new List<T>();
}
list.Add(item);
prev = item;
}
yield return list;
}
}
Next where ever you want to load these participants at you can throw this in:
Participant participant = new Participant(); // create new instance
participant.GetParticipants(); // actual grab the file and parse it
// here we actually group our participants based on your condition
var query = participant.Participants.GroupBy(p => p.ParticipantTimeSlot).Select(pNew => new { SlotNumber = pNew.ToList()[0].ParticipantTimeSlot, Count = pNew.Count() });
// finally write all the data out
Console.WriteLine(string.Join(Environment.NewLine, query.Select(a => $"Total Member Registered for Slot {a.SlotNumber}: {a.Count}")));
Here's my output:
Based on this file structure:
Update
Here's a query to print their id's and names:
var getNames = participant.Participants.Select(pNew => new { PartName = pNew.ParticipantName, PartNumber = pNew.ParticipantNumber });
Console.WriteLine(string.Join(Environment.NewLine, getNames.Select(a => $"Participant {a.PartNumber} name: {a.PartName}")));
The output of this:
If there's something you don't understand please let my self know, again, comments are through-out code.
Side Note:
You may need to make sure to import a few namespaces as well:
References:
Grouping sequential blocks of data using Linq - Chris St Clair
Upvotes: 0
Reputation: 2485
I see that you have made a few edits to your original question and left comments on another answer which leaves this answer in need of a few adjustments. Specifically, you appear to want the results to be displayed in ascending order (from slot 1 to 3). You also said that:
I want also if a slot has not any appear in the file to display Number 2 appeared 0 times.
So here is my proposed solution:
public static void ReadNumbers()
{
string[] lines = File.ReadAllLines("text.TXT");
var groups = lines
.Where(line => line.StartsWith("Time"))
.Select(line => Int32.Parse(new String(line.Where(Char.IsDigit).ToArray())))
.GroupBy(number => number);
for(int i = 1; i <= 3; i ++)
{
var count = groups.FirstOrDefault(group => group.Key == i)?.Count() ?? 0;
Console.WriteLine($"Total Members Registered for Slot {i}: {count}");
}
}
Note: This code is untested but should work.
I would also like to add that it is generally not considered good etiquette to make changes to your question after accepting an answer, such that the changes require a change to said answer. Typically you would ask a new question in such a case.
Here's how I would do it:
public static void ReadNumbers()
{
string[] lines = File.ReadAllLines("text.TXT");
var groups = lines
.Where(line => line.StartsWith("Time"))
.Select(line => Int32.Parse(new string(line.Where(Char.IsDigit).ToArray())))
.GroupBy(number => number);
foreach(var group in groups)
Console.WriteLine($"{group.Key} appeared: {group.Count()} times");
}
Note that this approach assumes that your file follows the same format that you showed in your question.
It will also throw an error should your file have any occurrences of "Time" without also containing a number in the same line. For example, if your file contains a line like: "Time Slot: "
or "Time Slot: SomeValueThatIsNotANumber"
then it will throw.
Upvotes: 3
Reputation: 924
If you want to print every time slot even if they are not used, a general solution would be:
public static void ReadNumbers()
{
string[] lines = File.ReadAllLines("text.TXT");
// timeSlots[i] - how many members are registered in i-th time slot
// 4 is number of time slots minus 1 (we skip the 0th element for convenience)
int[] timeSlots = new int[4];
var groups = lines
.Where(line => line.StartsWith("Time"))
.Select(line => Int32.Parse(new string(line.Where(Char.IsDigit).ToArray())))
.GroupBy(number => number);
foreach (var group in groups)
{
// group.Key - occupied time slot number
// group.Count() - how many members in the occupied time slot
if (group.Key < timeSlots.Count())
{
timeSlots[group.Key] = group.Count();
}
}
for (int i = 1; i < timeSlots.Count(); i++)
{
Console.WriteLine($"Time slot {i} appeared: {timeSlots[i]} times");
}
}
Upvotes: 1