C# array for finding all the 2's - c#

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 :)

Related

Sort a List in which each element contains 2 Values

I have a text file that contains Values in this Format: Time|ID:
180|1
60 |2
120|3
Now I want to sort them by Time. The Output also should be:
60 |2
120|3
180|1
How can I solve this problem? With this:
var path = #"C:\Users\admin\Desktop\test.txt";
List<string> list = File.ReadAllLines(path).ToList();
list.Sort();
for (var i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
I got no success ...
3 steps are necessary to do the job:
1) split by the separator
2) convert to int because in a string comparison a 6 comes after a 1 or 10
3) use OrderBy to sort your collection
Here is a linq solution in one line doing all 3 steps:
list = list.OrderBy(x => Convert.ToInt32(x.Split('|')[0])).ToList();
Explanation
x => lambda expression, x denotes a single element in your list
x.Split('|')[0] splits each string and takes only the first part of it (time)
Convert.ToInt32(.. converts the time into a number so that the ordering will be done in the way you desire
list.OrderBy( sorts your collection
EDIT:
Just to understand why you got the result in the first place here is an example of comparison of numbers in string representation using the CompareTo method:
int res = "6".CompareTo("10");
res will have the value of 1 (meaning that 6 is larger than 10 or 6 follows 10)
According to the documentation->remarks:
The CompareTo method was designed primarily for use in sorting or alphabetizing operations.
You should parse each line of the file content and get values as numbers.
string[] lines = File.ReadAllLines("path");
// ID, time
var dict = new Dictionary<int, int>();
// Processing each line of the file content
foreach (var line in lines)
{
string[] splitted = line.Split('|');
int time = Convert.ToInt32(splitted[0]);
int ID = Convert.ToInt32(splitted[1]);
// Key = ID, Value = Time
dict.Add(ID, time);
}
var orderedListByID = dict.OrderBy(x => x.Key).ToList();
var orderedListByTime = dict.OrderBy(x => x.Value).ToList();
Note that I use your ID reference as Key of dictionary assuming that ID should be unique.
Short code version
// Key = ID Value = Time
var orderedListByID = lines.Select(x => x.Split('|')).ToDictionary(x => Convert.ToInt32(x[1]), x => Convert.ToInt32(x[0])).OrderBy(x => x.Key).ToList();
var orderedListByTime = lines.Select(x => x.Split('|')).ToDictionary(x => Convert.ToInt32(x[1]), x => Convert.ToInt32(x[0])).OrderBy(x => x.Value).ToList();
You need to convert them to numbers first. Sorting by string won't give you meaningful results.
times = list.Select(l => l.Split('|')[0]).Select(Int32.Parse);
ids = list.Select(l => l.Split('|')[1]).Select(Int32.Parse);
pairs = times.Zip(ids, (t, id) => new{Time = t, Id = id})
.OrderBy(x => x.Time)
.ToList();
Thank you all, this is my Solution:
var path = #"C:\Users\admin\Desktop\test.txt";
List<string> list = File.ReadAllLines(path).ToList();
list = list.OrderBy(x => Convert.ToInt32(x.Split('|')[0])).ToList();
for(var i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestClass {
public static void main(String[] args) {
List <LineItem> myList = new ArrayList<LineItem>();
myList.add(LineItem.getLineItem(500, 30));
myList.add(LineItem.getLineItem(300, 20));
myList.add(LineItem.getLineItem(900, 100));
System.out.println(myList);
Collections.sort(myList);
System.out.println("list after sort");
System.out.println(myList);
}
}
class LineItem implements Comparable<LineItem>{
int time;
int id ;
#Override
public String toString() {
return ""+ time + "|"+ id + " ";
}
#Override
public int compareTo(LineItem o) {
return this.time-o.time;
}
public static LineItem getLineItem( int time, int id ){
LineItem l = new LineItem();
l.time=time;
l.id=id;
return l;
}
}

C# Add counter for each number in loop

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.

How to perform word search using LINQ?

I have a list which contains the name of suppliers. Say
SuppId Supplier Name
----------------------------------
1 Aardema & Whitelaw
2 Aafedt Forde Gray
3 Whitelaw & Sears-Ewald
using following LINQ query
supplierListQuery = supplierListQuery.Where(x => x.SupplierName.Contains(SearchKey));
I can return records correctly in the following conditions,
1) If i am using search string as "Whitelaw & Sears-Ewald" it will return 3rd record.
2) If i am using "Whitelaw" or "Sears-Ewald" it will return 3rd record.
But how can i return 3rd record if i am giving search string as "Whitelaw Sears-Ewald". It always returns 0 records.
Can i use ALL to get this result, but i dont know how to use it for this particular need.
What I usually do in this situation is split the words into a collection, then perform the following:
var searchopts = SearchKey.Split(' ').ToList();
supplierListQuery = supplierListQuery
.Where(x => searchopts.Any(y=> x.SupplierName.Contains(y)));
This works for me:
IEnumerable<string> keyWords = SearchKey.Split('');
supplierListQuery = supplierListQuery
.AsParallel()
.Where
(
x => keyWords.All
(
keyword => x.SupplierName.ContainsIgnoreCase(keyword)
)
);
Thank you all for your quick responses. But the one which worked or a easy fix to handle this was timothyclifford's note on this. Like he said i alterd my answer to this
string[] filters = SearchKey.ToLower().Split(new[] { ' ' });
objSuppliersList = (from x in objSuppliersList
where filters.All(f => x.SupplierName.ToLower().Contains(f))
select x).ToList();
Now it returns the result for all my serach conditions.
Because "Whitelaw" appears in both you will get both records. Otherwise there is no dynamic way to determine you only want the last one. If you know you only have these 3 then append .Last() to get the final record.
supplierListQuery = supplierListQuery.Where(x => x.SupplierName.Contains(SearchKey.Split(' ')[0]));
You need to use some sort of string comparer to create your own simple Search Engine and then you can find strings that are most likely to be included in your result :
public static class SearchEngine
{
public static double CompareStrings(string val1, string val2)
{
if ((val1.Length == 0) || (val2.Length == 0)) return 0;
if (val1 == val2) return 100;
double maxLength = Math.Max(val1.Length, val2.Length);
double minLength = Math.Min(val1.Length, val2.Length);
int charIndex = 0;
for (int i = 0; i < minLength; i++) { if (val1.Contains(val2[i])) charIndex++; }
return Math.Round(charIndex / maxLength * 100);
}
public static List<string> Search(this string[] values, string searchKey, double threshold)
{
List<string> result = new List<string>();
for (int i = 0; i < values.Length; i++) if (CompareStrings(values[i], searchKey) > threshold) result.Add(values[i]);
return result;
}
}
Example of usage :
string[] array = { "Aardema & Whitelaw", "Aafedt Forde Gray", "Whitelaw & Sears-Ewald" };
var result = array.Search("WhitelawSears-Ewald", 80);
// Results that matches this string with 80% or more
foreach (var item in result)
{
Console.WriteLine(item);
}
Output: Whitelaw & Sears-Ewald
If you want an easy (not very handy) solution,
var result = supplierListQuery
.Select(x => normalize(x.SupplierName))
.Where(x => x.Contains(normalize(SearchKey)));
string normalize(string inputStr)
{
string retVal = inputStr.Replace("&", "");
while (retVal.IndexOf(" ") >= 0)
{
retVal = retVal.Replace(" ", " ");
}
return retVal;
}

Select interval linq

Is there some way with LINQ to select certain numbers with shortcut criteria.
Like this:
I have numbers from 1 to 10000.
My criteria is (4012..4190|4229), meaning take numbers between 4012 to 4190 and number 4229:
static int[] test(string criteria)
{
// criteria is 4012..4190|4229
// select numbers from lab where criteria is met
int[] lab = Enumerable.Range(0, 10000).ToArray();
return lab;
}
This should be enough for your case:
return lab.Where((int1) => (int1 >= 4012 && int1 <= 4190) || int1 == 4229).ToArray();
Also a quick way of parsing your criteria would be to use RegEx:
Regex r = new Regex(#"\d+");
MatchCollection m = r.Matches(criteria);
int start = int.Parse(m[0].Value);
int end = int.Parse(m[1].Value);
int specific = int.Parse(m[2].Value);
return lab.Where((int1) => (int1 >= start && int1 <= end) || int1 == specific).ToArray();
If your criteria is always a string, you need some way to parse it, to Func<int, bool, but it's not LINQ specific. In the end you'll need something like this:
Func<int, bool> predicate = Parse(criteria);
return lab.Where(predicate).ToArray();
where very basic implementation of Parse might look as follows:
public static Func<int, bool> Parse(string criteria)
{
var alternatives = criteria
.Split('|')
.Select<string, Func<int, bool>>(
token =>
{
if (token.Contains(".."))
{
var between = token.Split(new[] {".."}, StringSplitOptions.RemoveEmptyEntries);
int lo = int.Parse(between[0]);
int hi = int.Parse(between[1]);
return x => lo <= x && x <= hi;
}
else
{
int exact = int.Parse(token);
return x => x == exact;
}
})
.ToArray();
return x => alternatives.Any(alt => alt(x));
}
You can concatenate two sequenses
int[] lab = Enumerable.Range(4012, 4190-4012).Concat(Enumerable.Range(4229,1)).ToArray();
Update:
you need to parse incoming criteria first
static int[] test(string criteria)
{
// criteria is 4012..4190|4229
// select numbers from lab where criteria is met
// assume you parsed your criteria to 2 dimentional array
// I used count for second part for convience
int[][] criteriaArray = { new int[]{ 4012, 50 }, new int[]{ 4229, 1 } };
var seq = Enumerable.Range(criteriaArray[0][0], criteriaArray[0][1]);
for (int i = 1; i < criteriaArray.Length; i++)
{
int start = criteriaArray[i][0];
int count = criteriaArray[i][1];
seq = seq.Concat(Enumerable.Range(start, count));
}
return seq.ToArray();
}
You could :
Flatten[{Range[4012, 4190], 4229}]
And in some way this would work as well 4012..4190|4229, but answer is exactly that - list of items from 4012 to 4190 and item 4229.
Lambda just imitates pure functions. However unless you have free wolfram kernel, using this approach might no be most cost effective. However, you do not need to write boilerplate code.

Split string into List<int> ignore none int values

Am using the following code to split a string into a List<int>, however occasionally the string includes non integer values, which are handled differently.
An example string might be like: 1,2,3,4,x
code looks like:
List<int> arrCMs = new List<int>(strMyList.Split(',').Select(x => int.Parse(x)));
The problem is as soon as it hits the 'x' it throws an error because 'x' can't be parsed as an integer.
How can I make it ignore non integer values? I'm sure I should be able to do something with int.TryParse but can't quite figure it out.
Thanks
List<int> arrCMs = strMyList.Split(',')
.Select(possibleIntegerAsString => {
int parsedInteger = 0;
bool isInteger = int.TryParse(possibleIntegerAsString , out parsedInteger);
return new {isInteger, parsedInteger};
})
.Where(tryParseResult => tryParseResult.isInteger)
.Select(tryParseResult => tryParseResult.parsedInteger)
.ToList();
The first Select in the above example returns an anonymous type that describes the result of int.TryParse - that is, whether it was a valid integer, and if so, what the value was.
The Where clause filters out those that weren't valid.
The second Select then retrieves the parsed values from the strings that were able to be parsed.
Short and sweet, using int.TryParse:
List<int> nums = list
.Split(',')
.Select(i =>
{
int val;
return int.TryParse(i, out val) ? (int?)val : null;
})
.Where(i => i.HasValue)
.Cast<int>()
.ToList()
Working Example: http://dotnetfiddle.net/4wyoAM
Change this
int result;
List<int> arrCMs =
new List<int>(strMyList.Split(',')
.Where(x => int.TryParse(x, out result))
.Select(int.Parse));
another one, using Array.ForEach
List<int> ints = new List<int>();
Array.ForEach(strMyList.Split(','), s =>
{
int i;
if (int.TryParse(s, out i)){ ints.Add(i);}
});
Plot twist: use an old-school foreach loop.
List<int> arrCMs = new List<int>();
foreach (string str in strMyList.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
int res;
if (int.TryParse(str, out res))
{
arrCMs.Add(res);
}
}
You could also create a method for it and make use of an iterator block by using yield return:
public static IEnumerable<int> ParseIntegers(string val, char seperator = ',')
{
foreach (string str in val.Split(new [] { seperator }, StringSplitOptions.RemoveEmptyEntries))
{
int res;
if (int.TryParse(str, out res))
{
yield return res;
}
}
}
This is probably over-engineering if it's only for one time use.
Parse once only for each value, but a bit tricky.
int result = 0;
List<int> arrCMs = strMyList.Split(',')
.Where(x => int.TryParse(x, out result))
.Select(x => result)
.ToList();

Categories