C# Add counter for each number in loop - c#

I'm writing a program that takes user data values 1-10 and stores it into an array. I'm trying to figure out how to add a counter that will allow me to output how many times each number was typed and how many invalid numbers were typed.
class Program
{
static void Main(string[] args)
{
int[] data = GetData();
}
private static int[] GetData()
{
int[] dataArray = new int[100];
int n = 0;
int intValue = 0;
while (true)
{
Console.WriteLine("Enter a number 0-10 (Q to end)");
string lineValue = Console.ReadLine();
if (lineValue.ToLower().Trim().Equals("q"))
{
break;
}
if (!int.TryParse(lineValue, out intValue))
{
Console.WriteLine("INVALID DATA - Try again.");
continue;
}
if (intValue < 0 || intValue > 10)
{
Console.WriteLine("NUMERIC DATA OUT OF RANGE - Try again.");
continue;
}
dataArray[++n] = intValue;
dataArray[0] = n;
}
return dataArray;
}
}

You could setup a counter before the while condition for the invalid numbers.
int invalidNumbersCounter = 0;
Then you could increment this counter each time the input is invalid:
if (!int.TryParse(lineValue, out intValue))
{
Console.WriteLine("INVALID DATA - Try again.");
invalidNumbersCounter++;
continue;
}
Regarding your other question, how many times each number, you could just use plain LINQ to do the trick.
static void Main(string[] args)
{
int[] data = GetData();
var statistics = data.GroupBy(x=>x)
.Select(gr => new
{
Number = gr.key,
Times = gr.Count()
});
foreach(var statistic in statistics)
{
Console.WriteLine(String.Format("The number {0} found {1} times in you array", statistic.Number, statistic.Times));
}
}
The result of the above query would be a sequence of objects of an anonymous type with two properties, the number and the number that this number is in the array.
Update
In order we avoid count the values that haven't been entered by the user and essentially are the initial values of the array, we could initialized the values of the array to -1, when we create the array, like below:
int[] dataArray = Enumerable.Repeat(-1, 100).ToArray();
Then we have to update our linq query to the following one:
var statistics = data.Skip(1)
.Where(number => number!=-1)
.GroupBy(x=>x)
.Select(gr => new
{
Number = gr.key,
Times = gr.Count()
});

You could do this:
public class Program
{
public static void Main(string[] args)
{
// note updated to use a list rather than array (just preference)
List<int> data = GetData();
}
private static List<int> GetData()
{
List<int> list = new List<int>();
int intValue = 0;
int invalidAttempts = 0; // added to keep track of invalid values
while (true)
{
Console.WriteLine("Enter a number 0-10 (Q to end)");
string lineValue = Console.ReadLine();
if (lineValue.ToLower().Trim().Equals("q"))
{
break;
}
if (!int.TryParse(lineValue, out intValue))
{
Console.WriteLine("INVALID DATA - Try again.");
invalidAttempts++;
continue;
}
if (intValue < 0 || intValue > 10)
{
Console.WriteLine("NUMERIC DATA OUT OF RANGE - Try again.");
invalidAttempts++;
continue;
}
list.Add(intValue);
}
Console.WriteLine("Invalid attempts {0}", invalidAttempts);
// this is using linq to group by the individual numbers (keys),
// then creates an anon object for each key value and the number of times it occurs.
// Create new anon object numbersAndCounts
var numbersAndCounts = list
// groups by the individual numbers in the list
.GroupBy(gb => gb)
// selects into a new anon object consisting of a "Number" and "Count" property
.Select(s => new {
Number = s.Key,
Count = s.Count()
});
foreach (var item in numbersAndCounts)
{
Console.WriteLine("{0} occurred {1} times", item.Number, item.Count);
}
return list;
}
}
note I'm using a list rather than array, i find them easier to work with.
Working demo:
https://dotnetfiddle.net/KFz1UY
Is it possible to make the output go in numeric order? Right now it just displays whatever numbers were typed first. Such as 1, 7, 7, 4 would be 1:1 7:2 4:1, how can I change this to go 1:1 4:1 7:2?
Sure is possible! See below (and updated original demo)
I just changed:
var numbersAndCounts = list
.GroupBy(gb => gb)
.Select(s => new {
Number = s.Key,
Count = s.Count()
});
to:
var numbersAndCounts = list
.GroupBy(gb => gb)
.Select(s => new {
Number = s.Key,
Count = s.Count()
})
.OrderBy(ob => ob.Number);

You already have an example of a counter (n) that you can use as an example to record the number of invalid inputs. Simply increment it inside this if block:
if (intValue < 0 || intValue > 10)
{
Console.WriteLine("NUMERIC DATA OUT OF RANGE - Try again.");
myInvalidCounter ++;
continue;
}
To get a count of each number entered - you can analyse the data stored inside your dataArray.
EDIT: I just noticed you have two examples of invalid data - bad raw data and out of range data - you need to set the counters as appropriate - maybe one each or a shared one etc.

Related

C# - LINQ Select returns a value but the variable is still null

private static readonly List<long> KnownPrimes = new List<long>() { 2, 3, 5, 7};
static void Main(string[] args)
{
int numDivisors;
string input = "";
bool first = true;
while (!int.TryParse(input, out numDivisors))
{
if(!first) Console.WriteLine("You must enter a number with no other characters.");
Console.WriteLine("Find the least common multiple for numbers 1 through:");
input = Console.ReadLine();
first = false;
}
int index = -1;
//make sure that there are enough primes in the list
while (index == -1)
{
index = KnownPrimes.FindIndex(n => n > numDivisors);
if(index == -1) AppendNextPrime();
}
// prep the list with 0s
List<int> countPrimes = KnownPrimes.Select(n=>0) as List<int>;
When I debug that last line in Rider, it is showing:
Enumerable.Select() returned: Count = 5 countPrimes: null
From what I have read, LINQ shouldn't be able to return a null, and it doesn't appear to be, but somehow the variable is remaining null. I'm obviously missing something here, can anyone help me identify what I am doing wrong?
as operator will return null since the result of the Select ist no List<int> but an IEnumerable<int>. Replace it by ToList to make a List out of the IEnumerable:
List<int> countPrimes = KnownPrimes.Select(n=>0).ToList();

collect the amount of correct number of guessing 4 digit number game

I want to know how to code if I want to know the amount of correct number although dislocating. For example, correct is "7896" but I put "8749" so I want it show that it has "3"(789) that is correct.
void Run()
{
// initialize the number of attempts
int numberOfAttempts = 1000;
Console.WriteLine("\nWelcome to Random Number Guessing Game.");
Console.WriteLine("\n\nGuess the 4 digit random number XXXX.");
Console.WriteLine("\nFor each digit, the number is chosen from 1 to 9 \nNumbers can repeat.");
// Call the method to Generate the Random Number
string randomNumber = GenerateRandomNumber();
for (int i = 1; i <= numberOfAttempts; i++)
{
// Call the method to get the user input
string userInput = GetUserInput(i);
// Get the result - Collection containing the result of all 4 digits
List<Result> result = GetResult(randomNumber, userInput);
// Guess the count of number of digits that are correct
int flagCount = result.Where(f => f.Flag == true).Count();
// Get the place(s)/index of digits that are correct
string digitsCorrect = string.Join(",", result.Where(f => f.Flag == true)
.Select(c => (++c.Index).ToString()));
// check the flag count and display appropriate message
if (flagCount == 4)
{
Console.WriteLine("Random Number:{0} , Your Input:{1}", randomNumber, userInput);
Console.WriteLine("You guess is correct! Game Won..hurray...:)");
break;
}
else
{
digitsCorrect = flagCount == 0 ? "none" : digitsCorrect;
Console.WriteLine(string.Format("Digit(s) in place {0} correct", digitsCorrect));
Console.WriteLine(flagCount);
}
}
Console.ReadLine();
}
i have already done some method and can play already
Something like this?
public static string Info(string guess, string actual)
{
int correctNumbers = 0;
string correctChars = "";
List<char> charlists = actual.ToList();
foreach (var char_ in guess)
{
if (charlists.Contains(char_))
{
correctNumbers++;
correctChars += char_.ToString();
charlists.Remove(char_);
}
}
return $"{correctNumbers}({correctChars})";
}
What you want to do is keep track of which digits have already been guessed. You can do that by creating a dictionary of the digits and checking how many are left unguessed
var guess = "7896";
var actual = "8749";
var charCounts = actual.GroupBy(x => x).ToDictionary(x => x.Key, x => x.Count());
var correct = new List<char>();
foreach(char c in guess)
{
if(charCounts.TryGetValue(c, out var count) && count > 0)
{
correct.Add(c);
charCounts[c]--;
}
}
Console.WriteLine($"You guessed {correct.Count} correct digits of {string.Join(",", correct)}");
This will then handle the cases of duplicate digits in the actual value and duplicate digits in the guess.
Try something like this:
var input = 8749;
var goal = 7896;
var inputDigits = input.ToString().Select(digit => int.Parse(digit.ToString())).ToList();
var goalDigits = goal.ToString().Select(digit => int.Parse(digit.ToString())).ToList();
var correctDigits = new List<int>();
for (int i = 0; i < goalDigits.Count(); i++)
{
if(inputDigits.Contains(goalDigits[i]))
{
correctDigits.Add(goalDigits[i]);
}
}
var result = new { correctDigits.Count, correctDigits };
Yet another way:
string randomNumber = "7896";
string userInput = "8749";
int result = randomNumber.GroupBy(x => x)
.Sum(x => Math.Min(userInput.GroupBy(y => y)
.FirstOrDefault(y => y.Key == x.Key)?.Count() ?? 0, x.Count()));

How to find the placement of a List within another List?

I am working with two lists. The first contains a large sequence of strings. The second contains a smaller list of strings. I need to find where the second list exists in the first list.
I worked with enumeration, and due to the large size of the data, this is very slow, I was hoping for a faster way.
List<string> first = new List<string>() { "AAA","BBB","CCC","DDD","EEE","FFF" };
List<string> second = new List<string>() { "CCC","DDD","EEE" };
int x = SomeMagic(first,second);
And I would need x to = 2.
Ok, here is my variant with old-good-for-each-loop:
private int SomeMagic(IEnumerable<string> source, IEnumerable<string> target)
{
/* Some obvious checks for `source` and `target` lenght / nullity are ommited */
// searched pattern
var pattern = target.ToArray();
// candidates in form `candidate index` -> `checked length`
var candidates = new Dictionary<int, int>();
// iteration index
var index = 0;
// so, lets the magic begin
foreach (var value in source)
{
// check candidates
foreach (var candidate in candidates.Keys.ToArray()) // <- we are going to change this collection
{
var checkedLength = candidates[candidate];
if (value == pattern[checkedLength]) // <- here `checkedLength` is used in sense `nextPositionToCheck`
{
// candidate has match next value
checkedLength += 1;
// check if we are done here
if (checkedLength == pattern.Length) return candidate; // <- exit point
candidates[candidate] = checkedLength;
}
else
// candidate has failed
candidates.Remove(candidate);
}
// check for new candidate
if (value == pattern[0])
candidates.Add(index, 1);
index++;
}
// we did everything we could
return -1;
}
We use dictionary of candidates to handle situations like:
var first = new List<string> { "AAA","BBB","CCC","CCC","CCC","CCC","EEE","FFF" };
var second = new List<string> { "CCC","CCC","CCC","EEE" };
If you are willing to use MoreLinq then consider using Window:
var windows = first.Window(second.Count);
var result = windows
.Select((subset, index) => new { subset, index = (int?)index })
.Where(z => Enumerable.SequenceEqual(second, z.subset))
.Select(z => z.index)
.FirstOrDefault();
Console.WriteLine(result);
Console.ReadLine();
Window will allow you to look at 'slices' of the data in chunks (based on the length of your second list). Then SequenceEqual can be used to see if the slice is equal to second. If it is, the index can be returned. If it doesn't find a match, null will be returned.
Implemented SomeMagic method as below, this will return -1 if no match found, else it will return the index of start element in first list.
private int SomeMagic(List<string> first, List<string> second)
{
if (first.Count < second.Count)
{
return -1;
}
for (int i = 0; i <= first.Count - second.Count; i++)
{
List<string> partialFirst = first.GetRange(i, second.Count);
if (Enumerable.SequenceEqual(partialFirst, second))
return i;
}
return -1;
}
you can use intersect extension method using the namepace System.Linq
var CommonList = Listfirst.Intersect(Listsecond)

Searching for all combinations of numbers from a set of numbers which equal a target, code misses some but not all

I used this question Find out which combinations of numbers in a set add up to a given total which uses a C# example to attempt to find all the possible combinations of numbers which add up to a given number.
I used the code as provided and have attempted to understand it and I think I do, however, I can't figure out why this error is occurring.
Here is the setup:
public class Program
{
public static void Main(string[] args)
{
// subtotal list
List<double> totals = new List<double>(new double[] { 17.5, 14.3, 10.9, 7.8, 6.3, 3.8, 3.2, 2.7, 1.8, 1.0 });
List<double> totals2 = new List<double>(new double[] { 17.5, 14.3, 7.8, 6.3, 3.2, 1.8});
// get matches
List<double[]> results = Knapsack.MatchTotal(50.9, totals2);
// print results`
foreach (var result in results)
{
Console.WriteLine(string.Join(",", result));
}
Console.WriteLine("Done.");
Console.ReadKey();
}
}
and the main bulk of the code is unchanged from the example.
public class Knapsack
{
internal static List<double[]> MatchTotal(double theTotal, List<double> subTotals)
{
List<double[]> results = new List<double[]>();
while (subTotals.Contains(theTotal))
{
results.Add(new double[1] { theTotal });
subTotals.Remove(theTotal);
}
// if no subtotals were passed
// or all matched the Total
// return
if (subTotals.Count == 0)
return results;
subTotals.Sort();
double mostNegativeNumber = subTotals[0];
if (mostNegativeNumber > 0)
mostNegativeNumber = 0;
// if there aren't any negative values
// we can remove any values bigger than the total
if (mostNegativeNumber == 0)
subTotals.RemoveAll(d => d > theTotal);
// if there aren't any negative values
// and sum is less than the total no need to look further
if (mostNegativeNumber == 0 && subTotals.Sum() < theTotal)
return results;
// get the combinations for the remaining subTotals
// skip 1 since we already removed subTotals that match
for (int choose = 2; choose <= subTotals.Count; choose++)
{
// get combinations for each length
IEnumerable<IEnumerable<double>> combos = Combination.Combinations(subTotals.AsEnumerable(), choose);
// add combinations where the sum mathces the total to the result list
results.AddRange(from combo in combos
where combo.Sum() == theTotal
select combo.ToArray());
}
return results;
}
}
public static class Combination
{
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int choose)
{
return choose == 0 ? // if choose = 0
new[] { new T[0] } : // return empty Type array
elements.SelectMany((element, i) => // else recursively iterate over array to create combinations
elements.Skip(i + 1).Combinations(choose - 1).Select(combo => (new[] { element }).Concat(combo)));
}
}
I have provided the set of numbers which is in totals which are used to try and calculate the total.
I entered 50.9 as the total and return no results.
The second set of numbers totals2 is a set of numbers which add up to 50.9 and they are a subset of totals. This also fails to return a result for 50.9
For some reason the code is unable to find 50.9 in the master set or in the subset (which itself sums to 50.9).
I can't figure out why it does this, can anyone tell me?

C# array for finding all the 2's

Basically what I have to do is find a certain number, which in this case is 2, and see how many times I have that number in my program, I assumed that I would have to use a .GetValue(42) but it's not doing it right, the code I am using is
static int count2(int[] input)
{
return input.GetValue(2);
}
input is from a separate method, but it contains the values that I'm working with which is
int [] input = {1,2,3,4,5};
Not sure if you count specifically the number 2, or any number that contains the number 2.
For the later here's the easy way:
public int count2(int[] input) {
int counter = 0;
foreach(var i in input) {
if (i.ToString().Contains("2"))
{
++counter;
}
}
return counter;
}
You can do it with LINQ
input.Count(x=>x==2);
Array.GetValue() "gets the value at the specified position in the one-dimensional Array" which is not what you want. (in your example it will return 3 because that's the value at index 2 of your array).
You want to count the number of times a specific item is in the array. That's a matter of looping and checking each item:
var counter = 0;
foreach(var item in input)
{
if(item == 2)
{
counter++;
}
}
return counter;
to get a count do this
int [] inputDupes = {1,2,3,4,5,2};
var duplicates = inputDupes
.Select(w => inputDupes.Contains(2))
.GroupBy(q => q)
.Where(gb => gb.Count() > 1)
.Select(gb => gb.Key).Count();//returns an Int32 value
to see if there are duplicates of the number 2 then do the following
int [] inputDupes = {1,2,3,4,5,2};
var duplicates = inputDupes
.Select(w => inputDupes.Contains(2))
.GroupBy(q => q)
.Where(gb => gb.Count() > 1)
.Select(gb => gb.Key)
.ToList(); //returns true | false
if you want to do this based on any number then create a method and pass a param in where .Contains() extension method is being called
if you want to capture user input from Console you can do it this way as well
int [] inputDupes = {1,2,3,4,5,2};
Console.WriteLine("Enter a number to check for duplicates: ");
string input = Console.ReadLine();
int number;
Int32.TryParse(input, out number);
var dupeCount = inputDupes.Count(x => x == number);
Console.WriteLine(dupeCount);
Console.Read();
Yields 2 for the duplicate Count
static int count2(int[] input)
{
return input.Count(i => i == 2);
}
You could use a Func like this:
public Func<int[], int, int> GetNumberCount =
(numbers, numberToSearchFor) =>
numbers.Count(num => num.Equals(numberToSearchFor));
...
var count = GetNumberCount(input, 2);
Gotta' love a Func :)

Categories