C# Linq Dictionary IO - c#

How can I do this with LinQ?
I have a txt file.(Its about 100 lines long.)
6 7
0/3 # ##t#kon tu#i#do#b#n ko#yk####a#s##ttak###lk##ul$$$$#$#$$$####$$$$#$$$$$$#$$#$$$$$#$
I stored it in a Dictionary (The two lines).
alap = File.ReadAllLines("veetel.txt");
Dictionary<string,string> statisztikaDictionary = new Dictionary<string,string>();
for (int z = 1; z < alap.Length; z+=2)
{
statisztikaDictionary.Add(alap[z],alap[z-1]);
}
The first line so in this the 6 is the day of record the 7 is the pesons id who recorded.
I need to create a statistics about it so.
Write it out like this:
Day:Number of persons who made a record.
I counted so the person number and the day value varies from 0-11 but we "don't" know this.
The second line starts with a 0/3 that means 0 adult 3 child. I need to read 2 values a day and an ID and print out how many wolfs the person observed(In this case 3).
I am training for my final exam and I got stuck with this. Any help would be apriciated,

I'd suggest to create custom class which can hold/store related data. Let it be Statisztika with the following fields/properties: Day, PersonId, Visitor and CountOfVisits.
Statisztika class definition:
public class Statisztika
{
private int iday = 0;
private int ipersonid = 0;
private int ivisitor =0;
private int icount =0;
//class constructor
public Statisztika(string[] twolines)
{
iday = Convert.ToInt32(twolines[0].Split(' ')[0]);
ipersonid = Convert.ToInt32(twolines[0].Split(' ')[1]);
//we have to replace these lines:
//ivisitor = Convert.ToInt32(twolines[1].Split('/')[0]);
//icount = Convert.ToInt32(twolines[1].Split('/')[1].Split(' ')[0]);
//with:
//check for single slash
int pos = twolines[1].IndexOf("/");
if (pos>-1)
{
//in case of error TryParse method returns zero
Int32.TryParse(twolines[1].Substring(0,pos)
.Replace("#", "").Trim(), out ivisitor);
Int32.TryParse(twolines[1].Substring(pos+1,2)
.Replace("#","").Trim(), out icount);
}
}
public int Day
{
get {return iday;}
set {iday = value;}
}
public int PersonId
{
get {return ipersonid;}
set {ipersonid = value;}
}
public int Visitor
{
get {return ivisitor;}
set {ivisitor = value;}
}
public int CountOfVisits
{
get {return icount;}
set {icount = value;}
}
}
As you can see, to create Statisztika object, you need to pass two lines of text (from the file) to be able to initiate fields. Now, we need to create List<Statisztika>. Imagine, it's data container. To get data from this list, we can use Linq query.
Usage:
string sFileName = #"D:\veetel.txt";
string[] alap = File.ReadAllLines(sFileName);
List<Statisztika> stat = new List<Statisztika>();
for(int i=0; i<alap.Length; i+=2)
{
//Linq rules!
string[] twolines = alap.Skip(i).Take(2).ToArray();
//create new Statisztika object and add to list
stat.Add(new Statisztika(twolines));
}
//use create linq query, group data by day and visitor type ;)
var qry = stat
.GroupBy(p=>new{p.Day, p.Visitor})
.Select(grp=>new
{
Day = grp.Key.Day,
Visitor = grp.Key.Visitor,
SumOfVisits = grp.Sum(p=>p.CountOfVisits)
})
.OrderBy(a=>a.Day)
.ThenBy(a=>a.Visitor);
Console.WriteLine("Day Visitor SumOfVisits");
foreach(var sta in qry)
{
Console.WriteLine("{0}\t{1}\t{2}", sta.Day, sta.Visitor, sta.SumOfVisits);
}
Sample output:
Day Visitor SumOfVisits
1 0 0
2 0 0
2 1 0
3 0 0
3 2 15
4 0 0
5 0 0
6 0 12
7 0 9
7 1 9
8 0 0
9 0 0
9 1 0
10 0 0
11 0 0
11 1 0
11 3 0
11 13 0
[EDIT]
Note: If \ (slash) does not exists, Statisztika class constructor uses zero's in Visitor and CountOfVisits fields/members.
If you would like to check if code works properly, use this code:
string sFileName = #"D:\veetel.txt";
string[] alap = File.ReadAllLines(sFileName);
for(int i=0; i<alap.Length; i+=2)
{
int one = 0;
int two = 0;
string[] twolines = alap.Skip(i).Take(2).ToArray();
int pos = twolines[1].IndexOf("/");
if(pos>-1)
{
Int32.TryParse(twolines[1].Substring(0,pos)
.Replace("#", "").Trim(), out one);
Int32.TryParse(twolines[1].Substring(pos+1,2)
.Replace("#","").Trim(), out two);
}
Console.WriteLine("{0}\t{1}\t{2}",
twolines[1].Substring(0,8), one, two);
}
Above code produces output as follow:
#abor# # 0 0
ta###t## 0 0
0/# a #a 0 0
a pat#k# 0 0
e#zakrol 0 0
1/3#sot# 1 3
0/# a pa 0 0
#3/0#s#t 3 0
verofe#y 0 0
ta#o#t#v 0 0
eszakro# 0 0
#/3#so## 0 3
...etc.
Cheers, Maciej

I guess you prefer some guidance instead of a working code as the full solution.
To be able to query a dictionary for the amount of persons that recorded observations in certain day, and to query for the amount of wolves observed by a person in certain day, I would create a dictionary with a key composed by day and personId and value as observation data.
See below for an example of 2 classes to hold keys and values for your dictionary.
For keys:
public class ObservationId
{
public int Day;
public int PersonId;
public ObservationId(int day, int personId)
{
this.Day = day;
this.PersonId = personId;
}
public ObservationId(string line)
{
// Add code here to split data in the line to fill day and personId values
}
}
For values:
public class ObservationData
{
public int Adults;
public int Childs;
public int TotalWolves
{
get { return this.Adults + this.Childs; }
}
public ObservationData(int adults, int childs)
{
this.Adults = adults;
this.Childs = childs;
}
public ObservationData(string line)
{
// Add code here to split data in the line to fill values for adults and childs (and optionally the rest of data)
}
}
To fill data from the file:
string[] alap;
alap = File.ReadAllLines("veetel.txt");
Dictionary<ObservationId, ObservationData> statisztikaDictionary = new Dictionary<ObservationId, ObservationData>();
for (int z = 1; z < alap.Length; z += 2)
{
ObservationId id = new ObservationId(alap[z - 1]);
ObservationData data = new ObservationData(alap[z]);
statisztikaDictionary.Add(id, data);
}
To search by day and person ID:
public int GetTotalWolves(int day, int personId)
{
ObservationId id = new ObservationId(day, personId);
if (statisztikaDictionary.ContainsKey(id))
{
return statisztikaDictionary[id].TotalWolves;
}
else
{
return 0;
}
}
To search for a day (using Linq):
public int GetObservationsByDay(int day)
{
return statisztikaDictionary.Where(o => o.Key.Day == day).Count();
}

Related

Split input with two termination numbers into two arrays c#

Lets assume we have an input from keyboard.
I dont know the length of the input, but I know that the first part is an array of integers, where consecutive groups of three numbers describe assets: type,value,amount.
then termination number is -1 then I have another array of integers and then again termination number -1
For example:
1 500 5 2 25 100 3 10 50 -1 3 9 -1
So I want to have 2 containers to work with first is {(1,500,5),(2,25,100),(3,10,50)} and second is {3,9}.
How I can make this happen?
This will give you a List of structs containing the asset data triples from the first part of the string, and an array of ints from the second part:
public struct assetData {
public int type;
public int value;
public int amount;
};
void Main()
{
string keyboard = "1 500 5 2 25 100 3 10 50 -1 3 9 -1";
string[] parts = keyboard.Split("-1", StringSplitOptions.RemoveEmptyEntries);
int[] assetInput = parts[0].Trim().Split().Select(int.Parse).ToArray();
int[] numbers = parts[1].Trim().Split().Select(int.Parse).ToArray();
// take 3 numbers at a time into assetData structs. Store them.
var assetInfo = new List<assetData>();
for (int i = 0; i < assetInput.Length; i += 3)
{
assetData item;
item.type = assetInput[i];
item.value = assetInput[i+1];
item.amount = assetInput[i+2];
assetInfo.Add(item);
}
}
variant for array input:
public struct assetData {
public int type;
public int value;
public int amount;
};
void Main()
{
string keyboard = "1 500 5 2 25 100 3 10 50 -1 3 9 -1";
int[] input = keyboard.Trim().Split().Select(int.Parse).ToArray();
// 3 numbers at a time into assetData structs. Store them.
var assetInfo = new List<assetData>();
int i;
for (i=0; input[i] != -1; i += 3)
{
assetData item;
item.type = input[i];
item.value = input[i+1];
item.amount = input[i+2];
assetInfo.Add(item);
}
i++; // skip the -1
var numbers = new List<int>();
while (i < input.Length - 1) {
numbers.Add(input[i]);
i++;
}
}

Show a pyramid based on number input

I'm currently stuck on this problem the goal is to be able to print out a pyramid depending on the input number.
Ex: input = 3
It should look like this:
1
23
I'm able to make it work with some numbers but it doesn't work with other inputs.
The current logic that I follow is by dividing the input by 2 then add the remainder to the next one to be divided.
Example input: 10
10 / 2 = 5 remainder: 0
5 / 2 = 2 remainder: 1
2 + 1 (previous remainder)/ 2 = 1 remainder: 1
1
Whereas it would look like this:
5
2
2
1
The rules of the pyramid that I'm trying to make only needs to have one or two differences per row.
That's why I'll be needing to deduct 1 from 5 and add it to the next one:
4
3
2
1
Thus having pyramid like this: 10
****
***
**
*
The problem is this approach isn't applicable on other inputs and I'm having a hard time on finding a different approach for this.
This code works for your example, although it might be improved to detect whenever a number is not suitable to print a piramid.
static void Main(string[] args)
{
while (true)
{
Console.Write("Enter a number:");
int input = int.Parse(Console.ReadLine());
List<List<string>> piramid = new List<List<string>>();
int rowNumber = 1, dotsCount = 0;
while (dotsCount < input)
{
List<string> row = new List<string>();
for (int i = 1; i <= rowNumber && dotsCount < input; i++)
{
row.Add("*");
dotsCount++;
}
piramid.Add(row);
rowNumber++;
}
Console.WriteLine("Piramid");
Print(piramid);
Console.WriteLine("Piramid (inverted)");
PrintInverted(piramid);
}
}
static void Print(List<List<string>> piramid)
{
foreach (var item in piramid)
{
item.ForEach(Console.Write);
Console.Write("\n");
}
}
static void PrintInverted(List<List<string>> piramid)
{
for (int i = piramid.Count - 1; i >= 0; i--)
{
List<string> item = piramid[i];
item.ForEach(Console.Write);
Console.Write("\n");
}
}

Declaring dynamic variables inside for loop (C#)

I have a task when I type for example 2 in the console then I am supposed to parse the next two lines of strings. I am giving you a clear example:
2
J 1 1 1 1
B 0 0 0 1
So when i do a for loop like this and when I console write it only shows me the 2nd basilisk(B 0 0 0 1):
string basilisk;
int basilisk1D = 0;
int basilisk2D = 0;
int basilisk3D = 0;
int basilisk4D = 0;
string basiliskName = string.Empty;
for (int i = 0; i < numberOfBasilisks; i++)
{
basilisk = Console.ReadLine();
var splittedBasilisk = basilisk.Split(' ');
basiliskName = splittedBasilisk[0];
basilisk1D = int.Parse(splittedBasilisk[1]);
basilisk2D = int.Parse(splittedBasilisk[2]);
basilisk3D = int.Parse(splittedBasilisk[3]);
basilisk4D = int.Parse(splittedBasilisk[4]);
}
It erases me the first line and only put the data of the second basilisk so can I put in the for loop i in the name of the variables to make them unique for each of basilisk or there is a better solution?
You cannot put eight integers and two strings into four int variables and one string variable. You need store the results in some sort of a collection.
First, create a class to store row data:
class Basilisk {
public string Name {get;set;}
public string Num1 {get;set;}
public string Num2 {get;set;}
public string Num3 {get;set;}
public string Num4 {get;set;}
}
Since you know right away how many rows you want, you can use arrays:
var basilisks = new Basilisk[numberOfBasilisks];
for (int i = 0; i < numberOfBasilisks; i++) {
basilisks[i] = new Basilisk();
var str = Console.ReadLine();
var splittedBasilisk = str.Split(' ');
basilisks[i].Name = splittedBasilisk[0];
basilisks[i].Num1 = int.Parse(splittedBasilisk[1]);
basilisks[i].Num2 = int.Parse(splittedBasilisk[2]);
basilisks[i].Num3 = int.Parse(splittedBasilisk[3]);
basilisks[i].Num4 = int.Parse(splittedBasilisk[4]);
}
You can also use collections, depending on whether or not you have progressed to using lists in your coursework.

Compare current and last value in list

There is a moving average suppose: 2, 4, 6 , 8 , 10...n;
Then add the current value (10) to list
List<int>numHold = new List<int>();
numhold.Add(currentvalue);
Inside the list:
the current value is added
10
and so on
20
30
40 etc
by using
var lastdigit = numHold[numhold.Count -1];
I can get the last digit but the output is
current: 10 last: 10
current: 20 last: 20
the output should be
current: 20 last: 10
Thanks
Typically, C# indexers start from 0, so the first element has index 0. On the other hand, Count/Length will use 1 for one element. So your
numHold[numhold.Count - 1]
actually takes the last element in the list. If you need the one before that, you need to use - 2 - though be careful you do not reach outside of the bounds of the list (something like Math.Max(0, numhold.Count - 2) might be appropriate).
You can also store the values in separate variables:
List<int> nums = new List<int> { 1 };
int current = 1;
int last = current;
for (int i = 0; i < 10; i++)
{
last = current;
current = i * 2;
nums.Add(current);
}
Console.WriteLine("Current: {0}", current);
Console.WriteLine("Last: {0}", last);
Question is so unclear, but if ur using moving average to draw a line graph 📈 you would use a circular buffer which can be implemented by urself utilizing an object that contains an array of specified size, and the next available position. You could also download a nuget package that already has it done.
A relatively simple way to calculate a moving average is to use a circular buffer to hold the last N values (where N is the number of values for which to compute a moving average).
For example:
public sealed class MovingAverage
{
private readonly int _max;
private readonly double[] _numbers;
private double _total;
private int _front;
private int _count;
public MovingAverage(int max)
{
_max = max;
_numbers = new double[max];
}
public double Average
{
get { return _total / _count; }
}
public void Add(double value)
{
_total += value;
if (_count == _max)
_total -= _numbers[_front];
else
++_count;
_numbers[_front] = value;
_front = (_front+1)%_max;
}
};
which you might use like this:
var test = new MovingAverage(11);
for (int i = 0; i < 25; ++i)
{
test.Add(i);
Console.WriteLine(test.Average);
}
Note that this code is optimised for speed. After a large number of iterations, you might start to get rounding errors. You can avoid this by adding to class MovingAverage a slower method to calculate the average instead of using the Average property:
public double AccurateAverage()
{
double total = 0;
for (int i = 0, j = _front; i < _count; ++i)
{
total += _numbers[j];
if (--j < 0)
j = _max - 1;
}
return total/_count;
}
Your last item will always be at position 0.
List<int>numHold = new List<int>();
numHold.add(currentvalue); //Adding 10
numHold[0]; // will contain 10
numHold.add(currentvalue); //Adding 20
numHold[0]; // will contain 10
numHold[numhold.Count - 1]; // will contain 20
the better way to get first and last are
numHold.first(); //Actually last in your case
numHold.last(); //first in your case

Calling methods side by side in a table

I'm making a table with Decimal, Binary, Octal and Hexadecimal columns. I wrote methods that display each of these types from a low number to a high number. My question is how do I display these methods side by side so they line up in the appropriate column?
I tried using the following, but it contains "invalid arguments":
Console.WriteLine("{0}\t{1}\t...", generate.buildDecimal(), generate.buildBinary(),...)
Here is my code:
using System;
public class Converter
{
int decimal0;
int decimal1;
int decimal2;
int decimal3;
int bottomLimit;
int topLimit;
// prompt user for range of numbers to display
public void PromptUser()
{
Console.Write("Enter bottom limit: ");
bottomLimit = Convert.ToInt32(Console.ReadLine());
decimal0 = bottomLimit;
decimal1 = bottomLimit;
decimal2 = bottomLimit;
decimal3 = bottomLimit;
Console.Write("Enter top limit: ");
topLimit = Convert.ToInt32(Console.ReadLine());
}
// display decimal values in range
public void buildDecimal()
{
while (decimal0 <= topLimit)
{
Console.WriteLine(decimal0);
decimal0++;
}
}
// display binary values in range
public void buildBinary()
{
while (decimal1 <= topLimit)
{
string binary = Convert.ToString(decimal1, 2);
Console.WriteLine(binary);
decimal1++;
}
}
// display octal values in range
public void buildOctal()
{
while (decimal2 <= topLimit)
{
string octal = Convert.ToString(decimal2, 8);
Console.WriteLine(octal);
decimal2++;
}
}
// display hexadecimal values in range
public void buildHex()
{
while (decimal3 <= topLimit)
{
string hex = Convert.ToString(decimal3, 16);
Console.WriteLine(hex);
decimal3++;
}
}
// call methods
public static void Main(string[] args)
{
Converter generate = new Converter();
generate.PromptUser();
Console.WriteLine("Decimal Binary Octal Hexadecimal");
generate.buildDecimal();
generate.buildBinary();
generate.buildOctal();
generate.buildHex();
}
}
As of right now, the program returns something like this:
Enter bottom limit: 1
Enter top limit: 10
Decimal Binary Octal Hexadecimal
1
2
3
4
5
6
7
8
9
10
1
10
11
100
101
110
111
1000
1001
1010
1
2
3
4
5
6
7
10
11
12
1
2
3
4
5
6
7
8
9
a
If you can't tell from my code, I'm a beginner, so please try not to go too far over my head. I'm actually supposed to put all of the methods in a separate class, but I couldn't figure out how to call the methods. I tried to call them in Converter with something like:
Converter callMethod = new Converter();
callMethod.buildDecimal();
But it didn't know what I was referring to.
Since the bottom limit/top limit are always the same you could just use a single method.
see below.
public void BuildNumbers()
{
while (decimal1 <= topLimit)
{
string binary = Convert.ToString(decimal1, 2);
string dec = Convert.ToString(decimal1);
string oct = Convert.ToString(decimal1, 8);
string hex = Convert.ToString(decimal1, 16);
Console.Write(binary);
Console.Write('\t');
Console.Write(dec);
Console.Write('\t');
Console.Write(oct);
Console.Write('\t');
Console.Write(hex);
Console.WriteLine();
decimal1++;
}
}

Categories