It looks like the core of your problem is taking two sequences and "interleaving" them, alternating elements for as long as possible, but allowing for the sequences to be different lengths. Your solution will definitely do that.
BUT. It would be cleaner if you were able to avoid building up a List
element-by-element, and if possible also avoid manual index management. My rule of thumb is "If you never access a list by index, you'll never have index-related bugs."
One way to do this would be to use enumerators. Your algorithm could look something like this:
while enumerator A has elements:
yield an element from enumerator A
if enumeratorB has another element:
yield an element from enumerator B
while enumerator B has elements:
yield an element from enumerator B
With this approach, you don't even need to know how many elements there are; you simply "go until you're done".
If I were writing this code, I would probably write it as an extension method, and call it like this:
private List<Child> MixGender(List<Child> children)
{
return children.Where(c => c.Sex == "male")
.Interleave(children.Where(c => c.Sex == "female"))
.ToList();
}
/// <summary>
/// Mix the elements of the two sequences, alternating elements
/// one by one for as long as possible. If one sequence has more
/// elements than the other, its leftovers will appear at the
/// end of the mixed sequence.
/// </summary>
public static IEnumerable<T> Interleave<T>(
this IEnumerable<T> sequenceA,
IEnumerable<T> sequenceB,
)
{
var enumA = sequenceA.GetEnumerator();
var enumB = sequenceB.GetEnumerator();
// As long as there are elements in sequence A
while (enumA.MoveNext())
{
// Take an element from sequence A
yield return enumA.Current;
// And, if possible,
if (enumB.MoveNext())
{
// Take an element from sequence B
yield return enumB.Current;
}
}
// If there are any elements left over in sequence B
while (enumB.MoveNext())
{
// Take each of them
yield return enumB.Current;
}
}
As for the question of time/resource demands: Is the invocation of an extension method, and handling of the enumerators more efficient then manual list creation and management? I would guess so, but I don't know, and I don't particularly care. I would be very confident in guessing that whatever performance gains you make by using one technique over the other will be dwarfed by, for example, the I/O operations your program is making. If (and only if) you notice performance degradation in your application, you should revisit the question with the help of a profiler.
Here is a demonstration comparing the behavior of your original implementation to the enumerator-based technique. They are almost the same but you may want to make some tweaks. It might be a good idea to adapt that demonstration into your unit test suite. Edit: As t3chb0t points out, it is also important to ensure your enumerators are Disposed, which this demo code fails to do.