How to populate two separate arrays from one comma-delimited list? - c#

I have a comma delimited text file that contains 20 digits separated by commas. These numbers represent earned points and possible points for ten different assignments. We're to use these to calculate a final score for the course.
Normally, I'd iterate through the numbers, creating two sums, divide and be done with it. However, our assignment dictates that we load the list of numbers into two arrays.
so this:
10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85
becomes this:
int[10] earned = {10,20,30,40,45,50,20,45,85};
int[10] possible = {10,20,35,50,50,50,20,90,85};
Right now, I'm using
for (x=0;x<10;x++)
{
earned[x] = scores[x*2]
poss [x] = scores[(x*2)+1]
}
which gives me the results I want, but seems excessively clunky.
Is there a better way?

The following should split each alternating item the list into the other two lists.
int[20] scores = {10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85};
int[10] earned;
int[10] possible;
int a = 0;
for(int x=0; x<10; x++)
{
earned[x] = scores[a++];
possible[x] = scores[a++];
}

You can use LINQ here:
var arrays = csv.Split(',')
.Select((v, index) => new {Value = int.Parse(v), Index = index})
.GroupBy(g => g.Index % 2,
g => g.Value,
(key, values) => values.ToArray())
.ToList();
and then
var earned = arrays[0];
var possible = arrays[1];

Get rid of the "magic" multiplications and illegible array index computations.
var earned = new List<int>();
var possible = new List<int>();
for (x=0; x<scores.Length; x += 2)
{
earned.Add(scores[x + 0]);
possible.Add(scores[x + 1]);
}
This has very little that would need a text comment. This is the gold standard for self-documenting code.
I initially thought the question was a C question because of all the incomprehensible indexing. It looked like pointer magic. It was too clever.
In my codebases I usually have an AsChunked extension available that splits a list into chunks of the given size.
var earned = new List<int>();
var possible = new List<int>();
foreach (var pair in scores.AsChunked(2)) {
earned.Add(pair[0]);
possible.Add(pair[1]);
}
Now the meaning of the code is apparent. The magic is gone.
Even shorter:
var pairs = scores.AsChunked(2);
var earned = pairs.Select(x => x[0]).ToArray();
var possible = pairs.Select(x => x[1]).ToArray();

I suppose you could do it like this:
int[] earned = new int[10];
int[] possible = new int[10];
int resultIndex = 0;
for (int i = 0; i < scores.Count; i = i + 2)
{
earned[resultIndex] = scores[i];
possible[resultIndex] = scores[i + 1];
resultIndex++;
}
You would have to be sure that an equal number of values are stored in scores.

I would leave your code as is. You are technically expressing very directly what your intent is, every 2nd element goes into each array.
The only way to improve that solution is to comment why you are multiplying. But I would expect someone to quickly recognize the trick, or easily reproduce what it is doing. Here is an excessive example of how to comment it. I wouldn't recommend using this directly.
for (x=0;x<10;x++)
{
//scores contains the elements inline one after the other
earned[x] = scores[x*2] //Get the even elements into earned
poss [x] = scores[(x*2)+1] //And the odd into poss
}
However if you really don't like the multiplication, you can track the scores index separately.
int i = 0;
for (int x = 0; x < 10; x++)
{
earned[x] = scores[i++];
poss [x] = scores[i++];
}
But I would probably prefer your version since it does not depend on the order of the operations.

var res = grades.Select((x, i) => new {x,i}).ToLookup(y=>y.i%2, y=>y.x)
int[] earned = res[0].ToArray();
int[] possible = res[1].ToArray();
This will group all grades into two buckets based on index, then you can just do ToArray if you need result in array form.

here is an example of my comment so you do not need to change the code regardless of the list size:
ArrayList Test = new ArrayList { "10,10,20,20,30,35,40,50,45,50,45,50,50,50,20,20,45,90,85,85" };
int[] earned = new int[Test.Count / 2];
int[] Score = new int[Test.Count / 2];
int Counter = 1; // start at one so earned is the first array entered in to
foreach (string TestRow in Test)
{
if (Counter % 2 != 0) // is the counter even
{
int nextNumber = 0;
for (int i = 0; i < Score.Length; i++) // this gets the posistion for the next array entry
{
if (String.IsNullOrEmpty(Convert.ToString(Score[i])))
{
nextNumber = i;
break;
}
}
Score[nextNumber] = Convert.ToInt32(TestRow);
}
else
{
int nextNumber = 0;
for (int i = 0; i < earned.Length; i++) // this gets the posistion for the next array entry
{
if (String.IsNullOrEmpty(Convert.ToString(earned[i])))
{
nextNumber = i;
break;
}
}
earned[nextNumber] = Convert.ToInt32(TestRow);
}
Counter++
}

Related

How do I add up Double Values from multiple String Arrays at the same position? C#

I get Strings like this from my database:
NaN#Nan#44.20216139610997#45.35340149990988#45.44329482112824#45.1593428796393#NaN#NaN
values = SQLvalues.Split('#'); //produces Array you can see in the picture
(String[] values)
Going on further with strings until it ends with about 10 "NaN" Strings again.
What I am doing now is that I sum up all the values from that one Array.
But there will be about 100 more Arrays after this one and I need to add up for example values[8] from this Array with the one at the same position from the next Array.
hope this visualizes better what I need to do
As I am still an apprentice I don´t have much knowledge on all of this.
I´ve been trying to come with a solution for several hours now but I won´t seem to get anything to work here.
Any help would be great!
My Code:
String[] values;
String returnString = "";
List<Double> valueList = new List<Double>();
DateTime time = (DateTime)skzAdapterText.MinTambourChangedTime();
DataTable profilData = skzAdapterText.LoadValuesText(time);
int rowCount = profilData.Rows.Count;
for (int i = 0; i < rowCount; i++)
{
String SQLvalues = (String)profilData.Rows[i][2];
values = SQLvalues.Split('#');
double summe = 0;
int counter = 0;
foreach (String tmpRow in values)
{
Double value;
if (double.TryParse(tmpRow, NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture, out value)
&& !double.IsNaN(value))
{
counter++;
summe = summe + value;
}
}
if (summe != 0 && counter != 0)
valueList.Add(summe / counter);
}
The basic sum can be reduced like so:
values = SQLvalues.Split('#');
double sum = values.Where(v => v != "NaN").Select(v => double.Parse(v)).Sum();
For a specific position, say index 8, within many rows:
//Read the data from DB
DataTable profilData = skzAdapterText.LoadValuesText(time);
//parse out the values
var rowValueArrays = // will be a List<double[]>
profilData.Rows.
Select(r => r[2].Split('#').Select(v => v == "NaN"?0.0:double.Parse(v)).ToArray()).
ToList();
// sum the entries at index 8
double sumAt8 = rowValueArrays.Select(r => r[8]).Sum();
You say you are an apprentice, and so the syntax here may be unfamiliar to you and seem difficult to understand. But I want to emphasize the power here. The combination of IEnumerable, lambda expressions, and linq operations reduced the original sample down to two lines of code, and solved the full problem in what is technically three lines (spread out a little for readability). If I wanted to sacrifice any sense of style or maintainability, we could do it in just one line of code.
In short, it is well worth your time to learn how to write code this way. With practice, reading and writing code this way can become easy and greatly increase your speed and capability as a programmer.
I also see attempts to compute an average. Continuing from the end of the previous code:
int countAt8 = rowValuesArrays.Count(r => r[8] != 0.0);
double average = sumAt8 / countAt8;
Finally, I need to point out delimited data like this in a column is an abuse of the database and very poor practice. Schemas like this are considered broken, and need to be fixed.
As you want to sum up the values at the same positions of the arrays, I assume that all these array have the same length. Then first declare the required arrays. You also must probably calculate the average for each array position, so you also need an array for the counter and the averages.
double[] average = null;
int rowCount = profilData.Rows.Count;
if (rowCount > 0) {
string[] values = ((string)profilData.Rows[0][2]).Split('#');
int n = values.Length;
double[] sum = new double[n];
double[] counter = new double[n];
for (int i = 0; i < rowCount; i++) {
values = ((string)profilData.Rows[i][2]).Split('#');
for (int j = 0; j < n; j++) {
if (double.TryParse(values[j], NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture, out double value) && !double.IsNaN(value)) {
counter[j]++;
sum[j] += value;
}
}
}
average = new double[n];
for (int i = 0; i < n; i++) {
if (counter[i] != 0) {
average[i] = sum[i] / counter[i];
}
}
}
You cannot calculate the average while summing up, since you must divide the total sum by the total count. Therefore, I added another loop calculating the averages at each array position after the summing phase.

Splitting a list c#

Hi I'm a little bit stuck in C#. I'm new on it.
This is my problem:
I have a list made up of 63 double numbers (let's call it "big list").
I want to split this list in 6 list with the following rule:
The first list consists of the first 8 elements of the big list;
The second list goes from the 9th element of the big list to the (9+8=17th) element of the big list;
The third list goes from the 18th element of the big list to the (18+8+1=27th) element of the big list;
The fourth list goes from the 28th element of the big list to the (28+8+2=38th) element of the big list;
The fifth list goes from the 39th element of the big list to the (39+8+3=50th) element of the big list;
The sixth list goes from the 51th element of the big list to the (51+8+4=63th) element of the big list;
How can I do it? thanks a lot in advance for your help!
i've tried in this way but it gives me error "cannot apply indexing with [] to an expression of type method group"
List listsplitted = new List();
for (int i = 0; i < 6; i++)
{
for (int j = 8; j < 8 + i + 1; j++)
{
Listsplitted[i] = biglist.Take[j];
}
}
It is a very simple approach with the IEnumerable extensions Skip and Take
List<double> first = bigList.Take(8).ToList();
List<double> second = bigList.Skip(8).Take(8).ToList();
List<double> third = bigList.Skip(16).Take(9).ToList();
List<double> fourth = bigList.Skip(25).Take(10).ToList();
List<double> fifth = bigList.Skip(35).Take(11).ToList();
// The last one is without Take to get every remaining element
List<double> sixth = bigList.Skip(46).ToList();
Of course you should check if the indexes are correct for your requirements. These indexes doesn't skip any elements from your bigList
You can make this approach more generic with something like this
void Main()
{
var bigList = GetYourBigList();
List<Tuple<int, int>> positions = new List<Tuple<int, int>>
{
new Tuple<int, int>(0,8),
new Tuple<int, int>(8,8),
new Tuple<int, int>(16,9),
new Tuple<int, int>(25,10),
new Tuple<int, int>(35,11),
new Tuple<int, int>(46,13)
};
List<List<int>> result = SplitTheList(bigList, positions);
foreach (var list in result)
{
foreach (var temp in list)
Console.WriteLine(temp);
Console.WriteLine("--------------------");
}
}
List<List<int>> SplitTheList(List<int> r, List<Tuple<int, int>> positions)
{
List<List<int>> result = new List<List<int>>();
foreach(var x in positions)
result.Add(r.Skip(x.Item1).Take(x.Item2).ToList());
return result;
}
You can do it using GetRange function:
List<double> part1= big_list.GetRange(0, 8);//Retrieves 8 items starting with index '0'
List<double> part2= big_list.GetRange(8, 9);//Retrieves 9 items starting with index '8'
Or if you don't want to give different names to all parts, you can create a list of lists:
List<List<double>> listoflists = new List<List<double>>();
listoflists.Add(big_list.GetRange(0, 8));//Retrieves 8 items starting with index '0'
listoflists.Add(big_list.GetRange(8, 9));//Retrieves 9 items starting with index '8'
for(int i=0; i<listoflists.Count;i++){
for(int j=0; j<listoflists[i].Count; j++){
Console.Write(listoflists[i][j] + " ");
}
Console.WriteLine();
}
if you are insisting on do the operation in one single for statement you can use the following code
const int arrSize = 6;
var splitedLists = new List<List<int>>(arrSize);
var defaultTake = 8;
var defaultSkip = 0;
for (var i = 1; i <= arrSize; i++)
{
if (i >= 3)
defaultSkip--;
splitedLists.Add(array.Skip(defaultSkip).Take(defaultTake + 1).ToList());
if (i >= 2)
defaultTake++;
defaultSkip = defaultSkip + defaultTake + 1;
}
If you can replace the list with an array, you can do without copying the data. You can use ArraySegment (or Span/Memory in newer versions of the language) to do this.
var array = Enumerable.Range(0, 63).Select(i => (double)i).ToArray();
var splitted = new List<ArraySegment<double>>();
int offset = 0;
int count = 8;
for (int i = 0; i < 6; i++)
{
splitted.Add(new ArraySegment<double>(array, offset, count));
offset += count;
count++;
}
// see what we have
foreach (var seg in splitted)
Console.WriteLine(seg.Offset + " " + seg.Count);
// work with segments
var segment = splitted[3];
segment[5] = 555555;
// the main array has also changed
Console.WriteLine(string.Join(" ", array));
Note that the segments do not store copies of the data, but refer to the data in the main array.

Way to extract positive and negative numbers from one array to another two (positive and negative)

I'm studying c# and I was wondering if there is any way to extract positive and negative numbers (integers) from one array to others two, one that contains the positive numbers and the other negative ones
I've tried something like
public static void Main(string[] args)
{
int[] num = new int[50];
Random objeto = new Random();
int i = 0;
for (i = 1; i <= 50; i++)
{
Console.WriteLine("Random numbers:");
num[1] = objeto.Next(-50, 50);
Console.WriteLine(num[1] + "");
}
Console.ReadKey(); here
}
I have to create two other arrays
int[] positive_numbers = int new [N]
int[] negative_numbers = int new [N]
And I guess I should create a method, but I do not know how I have to do it.
You could use LINQ:
var positiveNumbers = numbers.Where(n => n > 0).ToArray();
var negativeNumbers = numbers.Where(n => n < 0).ToArray();
Or an alternative approach is to count how many even and odd numbers you have, create two arrays and then populate them. This assumes that you want the arrays to be exactly the correct length.
// Variables to store counts of positive and negative numbers
int positiveCount = 0;
int negativeCount = 0;
// Because we'll be building new arrays, we need to track our
// position within them, so we create two variables to do that
int positiveIndex = 0;
int negativeIndex = 0;
// loop through once to count the positive and negative numbers
foreach (var number in numbers)
{
if (number > 0)
{
++positiveCount; // same as positiveCount = positiveCount + 1
}
else if (number < 0)
{
++negativeCount;
}
}
// now we know how many +ve and -ve numbers we have,
// we can create arrays to store them
var positiveNumbers = new int[positiveCount];
var negativeNumbers = new int[negativeCount];
// loop through and populate our new arrays
foreach (var number in numbers)
{
if (number > 0)
{
positiveNumbers[positiveIndex++] = number;
// number++ will return the value of number before it was incremented,
// so it will first access positiveNumbers[0] and then positiveNumbers[1], etc.
// each time we enter this code block.
}
else if (number < 0)
{
negativeNumbers[negativeIndex++] = number;
}
}
An alternative approach to the initial count would be to define both arrays to be the same length as the numbers array, and then use positiveIndex and negativeIndex to determine the maximum populated index in the positiveNumbers and negativeNumbers arrays. The downside is that it uses a little more memory (but memory is cheap for such a small set), and the upside is that you only have to loop through once so it's more performant.
If your situation allows, it might be easier to use generic lists instead:
var positiveNumbers = new List<int>();
var negativeNumbers = new List<int>();
foreach (var number in numbers)
{
if (number > 0)
{
positiveNumbers.Add(number);
}
else if (number < 0)
{
negativeNumbers.Add(number);
}
}
Generic lists are basically fancy wrappers around internal arrays. The list starts out with an array of a relatively small size. As you add items to the list, more arrays are generated to store all of your items. You can see the current overall size of the internal arrays by checking the list's .Capacity property. Do not confuse Capacity with Count. Count shows the number of items actually in your list, whereas Capacity shows the number of items your list can hold before expanding.
Note that in these answers, zeroes will be excluded since you only asked for positive and negative numbers, and zero is neither. As highlighted by Max Play's comment, you should change > to >= if you consider zero to be positive.
Assuming you'll handle 0 as positive, add a method to check for positive.
private static bool isNegtive(int number)
{
return number < 0;
}
I'd use list over array for unknown quantities. It'd go something like this:
public static void SeparateRandomNumbers()
{
IList<int> positive_numbers = new List<int>();
IList<int> negative_numbers = new List<int>();
Random objeto = new Random();
for (int i = 0; i < 50; i++)
{
var number = objeto.Next(-50, 50);
if (isNegtive(number))
{
negative_numbers.Add(number);
}
else
{
positive_numbers.Add(number);
}
}
}

Convert Ienumerable<int> to array and add to list C#

I have a simple code:
List<int[]> list = new List<int[]>();
for (int i = 0; i < x; i++)
{
var vec = vector.Skip(index).Take(width);
var v = vec.ToArray();
list.Add(v);
index = index + width;
}
string toDisplay = string.Join(Environment.NewLine, list);
MessageBox.Show(toDisplay);
This is vector:
int[] vector = new int[length];
Random z = new Random();
for (int i = 0; i < length; i++)
{
vector[i] = z.Next(-100, 100);
}
What I want to do is to slice my vector on smaller vectors and add them to list of int. Using my code I only get System.Int32[] in MessageBox. I know that maybe my code it's not the right way. I barely know C#.
How can I do this in other way?
Apparently you mean to slice the initial array into smaller chunks and display them in a single line. This can be done using Linq as follows.
var StringToDisplay
= String.Join(Environment.NewLine, list.Select(iList => String.Join(",", iList)));
List<int[]> list is a list of arrays, not numbers. Calling ToString() on an array uses Object.ToString() which returns the object's (array's) type.
If you want to display a list of pages, you should change your string construction code to work with the inner arrays. One option is to use LINQ :
var lines=from page in list
select string.Join(",", page);
string toDisplay = string.Join(Environment.NewLine, lines);
It's better to use StringBuilder though, to avoid generating a lot of temporary strings:
var builder=new StringBuilder();
foreach(var page in list)
{
builder.AppendLine(string.Join(",", page));
}
string toDisplay = builder.ToString();
If you want a list of numbers change the list type to List. You can also simplify the code by using AddRange, eg :
List<int> list = new List<int>();
for (int i = 0; i < x; i++)
{
var vec = vector.Skip(index).Take(width);
list.AddRange(vec);
index = index + width;
}
string toDisplay = string.Join(Environment.NewLine, lines);

How do I delete all elements in an int array that exist in another int array?

In my C# program, I have an int array containing a set of integers and occasionally duplicates of those integers. I want to create an array that only contains the numbers that exist as duplicates in the initial array, but in itself contains no duplicates. Based on my newbie understanding of C# I thought that the following code would do the trick:
int a = 9;
int b = 6;
int c = 3;
int index = 0;
int[] mltpls = new int[a + b + c];
while (a > 0)
{
mltpls[index] = 2 * a;
a -= 1;
index += 1;
}
while(b > 0)
{
mltpls[index] = 3 * b;
b -= 1;
index += 1;
}
while(c > 0)
{
mltpls[index] = 5 * c;
c -= 1;
index += 1;
}
int[] mltpls_unique = mltpls.Distinct().ToArray();
int[] mltpls_dplcts = mltpls.Except(mltpls_unique).ToArray();
Console.WriteLine(mltpls_dplcts);
//EDIT
//By running the following code I can write out all numbers in "mltpls"
for (int i = 0; i < mltpls.Length; i++)
{
Console.Write(mltpls[i] + ", ");
}
/*If I try to run equivalent code for the "mltpls_dplcts" array nothing
only a blank line is displayed.*/
When I run this goal my the final result of my console application is a blank row. My interpretation of this is that the array mltpls_dplcts is empty or that I'm incorrectly going about printing the array.
How do get only the duplicate values from an array?
My interpretation of this is that the array mltpls_dplcts is empty or that I'm incorrectly going about printing the array.
Both interpretations are correct
Distinct will return every item that is at least once present in mltps. If you now apply Except you get nothing because all items that are in mltpls_unique are also present in mltps. The items in the array are compared by value, so for Except it does not matter whether a number occurs multiple times in the other array. If it is there once it will not return the number. So you get an empty array.
Furthermore you cannot simply shove an entire array into Console.WriteLine. Either use a loop or String.Join to print the content:
Console.WriteLine(String.Join(" ",mltpls_dplcts));
Solution: You can solve it using a good old loop approach ;)
int[] mltpls_unique = mltpls.Distinct().ToArray();
// The amount of duplicates is the difference between the original and the unique array
int[] mltpls_dplcts = new int[mltpls.Length-mltpls_unique.Length];
int dupCount = 0;
for (int i = 0; i < mltpls.Length; i++)
{
for (int j = i+1; j < mltpls.Length; j++)
{
if (mltpls[i] == mltpls[j])
{
mltpls_dplcts[dupCount] = mltpls[i];
dupCount++;
}
}
}
Output: 18 12 10 6 15
You cannot print the array directly. You need to loop and print one by one:
foreach (var element in mltpls_dplcts)
{
Console.WriteLine(element);
}
You can get array of distinct duplicates like this:
var duplicates = mltpls.GroupBy(o => o)
.Where(g => g.Count() > 1)
.Select(g => g.First()).ToArray();
To get new array that contains only the elements from the original one that are not in the second array you can use:
var newArr = mltpls.Except(duplicates).ToArray();
It is not proper way to find duplicates. You can determine the duplicates by using GroupBy and print them to console like this;
var mltpls_dplcts = mltpls.GroupBy(x => x).Where(x => x.Count() > 1).Select(x => x.Key).ToArray();
foreach (var duplicate in mltpls_dplcts)
{
Console.WriteLine(duplicate);
}
Also, If it isn't must to use Array for you, I suggest you to use List<int>.
Updated question from OP:
How do get only the duplicate values from an array?
var arr1 = new[] {1, 2, 4, 4, 5, 5, 5, 5, 6, 7, 1};
var duplicates = arr1.ToLookup(_ => _, _ => _).Where(_ => _.Count()>1).Select(_ => _.Key).ToArray();
// duplicates is now { 1, 4, 5 }
Original question from OP:
How do I delete all elements in an int array that exist in another int array in C#?
var arr1 = new[] {1, 2, 4, 5, 6, 7};
var arr2 = new[] {4, 5};
var hash = new HashSet<int>(arr1);
hash.ExceptWith(arr2);
var filteredArray = hash.ToArray();
// filteredArray is now { 1, 2, 6, 7 }

Categories