简体   繁体   中英

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

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++

    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM