egyeneskanyar
egyeneskanyar

Reputation: 167

Creating a reference array from another array

My task is the next (in C#): I have a List with thousands of elements - each student has a name and an age (from 7-18). The important thing that this list is ordered, by the age, ascending. So the very first element would be like some of the 7 years old students. I have to write a search function soon (it doesn't really matter now), but in order to make the search function faster, I've been told to make a reference array which points to every first element of ages in my Students list, so actually this new array would contain 11 elements (let's say we have at least one child from each age group, which is most likely) since I would store the first elements of each years.

So whenever I would have to find a student who is for example 16 years old, I could start searching on the ref array first, get to the 16 yo student really fast, which points to Student list's 16 years old student's first element right on the spot without pointless iterating. My question is, how can I tell my Student[] refArray() to make a reference to every first "group of ages" in student list?

static Student[] refArray(List<Student> list_in)
    {
        int i = 7;
        Student[] refArray = new Student[11];
        // the size of the array is 11 since I'm assuming to get at least
        // 1 student of each age groups, but I could just use a List or anything

        foreach (Student stud in list_in)
        {
            if (stud.age == i)
            {
                refArray[i++] = stud;
            }
        }

        return refArray;
    }

I'm really sure this is code is as wrong as a bad code could be, but I have no idea how to solve this the easiest way. Another problem that I really don't know what if I found a first student with that age, how to make a ref which points to that object in student list...

Upvotes: 0

Views: 604

Answers (3)

phoog
phoog

Reputation: 43056

Both the answers so far are fairly sophisticated, using Linq. Since you're not allowed to use dictionaries, I suppose you're not allowed to use Linq, either. I think this answers the question in the intended spirit. Note that I renamed the method to reduce name clashes:

static int[] GetRefArray(List<Student> list_in)
{
    int[] refArray = new int[11];

    int referenceIndex = 0;
    int dataIndex = 0;
    int currentAge = int.MinValue;

    foreach (Student stud in list_in)
    {
        if (stud.age != currentAge)
        {
            currentAge = stud.age;
            refArray[referenceIndex++] = dataIndex;
        }
        dataIndex++;
    }

    return refArray;
}

Now there are all sorts of problems with this approach, as mentioned in the comments. There's an easy solution to handle the problem of list inserts: throw away the reference array whenever you insert into the list and recalculate the reference array whenever you need to use it:

//we're about to use refArray to improve our lookup performance
if (refArray == null)
    refArray = GetRefArray(studentList);
//use refArray here

and

//we're about to insert into the student list
refArray = null;
studentList.Insert(index, newStudent);

You could also modify the values in the refArray whenever you do an insert or remove. That could be tricky to get right, however, so I would only do it if performance testing indicated that it was necessary (or if it's a class assignment).

Upvotes: 1

BardMorgan
BardMorgan

Reputation: 454

For what it's worth, I agree that a better data structure would be the best solution. That said, you could use a LINQ query to do what you're looking at. Assuming your list of students is already sorted by age (and presumably by some other category such as alphabetically by last name within the age groups).

Assuming a Student class similar to:

public class Student
{
   public int Age {get; set;}
   public string Name {get; set;}
}

The code would be similar to:

var list = GetStudents();  // gets full list of students from somewhere, ordered by age, then name
var subList = new List<Student>();  // creates new empty list of students.

var ageList = list.Select(s => s.Age).Distinct().ToList();  // Gets a list of distinct age values
ageList.ForEach(s => subList.Add(list.First(p => p.Age == s)));

The variable subList would then contain a list of students with each student being the first in each age group. Getting the list of distinct ages in your list of students gets only ages that have at least one student, so your list.First should never encounter an exception.

Of course, one drawback to this is that, if your master list of students changes, you'll need to regenerate your subList.

Upvotes: 2

BradleyDotNET
BradleyDotNET

Reputation: 61379

The least complicated/easiest to understand way to accomplish this would be to start by populating a Dictonary<int, List<Student>> and doing your look up from there. However, since you already have a list, you can create a Lookup by using ToLookup

var lookup = studentsList.ToLookup(s => s.Age);

Then you can index in just like a dictionary. For example, to get all students with age 8:

var age8Students = lookup[8];

Here is a good explanation of this function: DotNetPerls

Upvotes: 3

Related Questions