Using a comment on the original question by @TVOHM, I have implemented the following code
public static int[] ReplaceUsingLinq(IEnumerable<int> arrayFromExisting, IEnumerable<int> x)
{
var indices = x.ToArray();
var i = 0;
var newArray = arrayFromExisting.Select(val =>
{
if (val != -1) return val;
var ret = indices[i];
i++;
return ret;
}).ToArray();
return newArray;
}
public static int[] ReplceUsingForLoop(int[] arrayExisting, IEnumerable<int> x)
{
var arrayReplacements = x.ToArray();
var replaced = new int[arrayExisting.Length];
var replacementIndex = 0;
for (var i = 0; i < arrayExisting.Length; i++)
{
if (arrayExisting[i] < 0)
{
replaced[i] = arrayReplacements[replacementIndex++];
}
else
{
replaced[i] = arrayExisting[i];
}
}
return replaced;
}
public static unsafe int[] ReplaceUsingPointers(int[] arrayExisting, IEnumerable<int> reps)
{
var arrayReplacements = reps.ToArray();
int replacementsLength = arrayReplacements.Length;
var replaced = new int[arrayExisting.Length];
Array.Copy(arrayExisting, replaced, arrayExisting.Length);
int existingLength = replaced.Length;
fixed (int* existing = replaced, replacements = arrayReplacements)
{
int* exist = existing;
int* replace = replacements;
int i = 0;
int x = 0;
while (i < replacementsLength && x < existingLength)
{
if (*exist == -1)
{
*exist = *replace;
i++;
replace++;
}
exist++;
x++;
}
}
return replaced;
}
public static int[] ReplaceUsingLoopWithMissingArray(int[] arrayExisting, IEnumerable<int> x,
int[] missingIndices)
{
var arrayReplacements = x.ToArray();
var replaced = new int[arrayExisting.Length];
Array.Copy(arrayExisting, replaced, arrayExisting.Length);
var replacementIndex = 0;
foreach (var index in missingIndices)
{
replaced[index] = arrayReplacements[replacementIndex];
replacementIndex++;
}
return replaced;
}
and benchmarked this using the following code:
public void BenchmarkArrayItemReplacements()
{
var rand = new Random();
var arrayExisting = Enumerable.Repeat(2, 1000).ToArray();
var arrayReplacements = Enumerable.Repeat(1, 100);
var toReplace = Enumerable.Range(0, 100).Select(x => rand.Next(100)).ToList();
toReplace.ForEach(x => arrayExisting[x] = -1);
var misisngIndices = toReplace.ToArray();
var sw = Stopwatch.StartNew();
var result = ArrayReplacement.ReplceUsingForLoop(arrayExisting, arrayReplacements);
Console.WriteLine($"for loop took {sw.ElapsedTicks}");
sw.Restart();
result = ArrayReplacement.ReplaceUsingLinq(arrayExisting, arrayReplacements);
Console.WriteLine($"linq took {sw.ElapsedTicks}");
sw.Restart();
result = ArrayReplacement.ReplaceUsingLoopWithMissingArray(arrayExisting, arrayReplacements, misisngIndices);
Console.WriteLine($"with missing took {sw.ElapsedTicks}");
sw.Restart();
result = ArrayReplacement.ReplaceUsingPointers(arrayExisting, arrayReplacements);
Console.WriteLine($"Pointers took {sw.ElapsedTicks}");
}
This gives the results:
for loop took 848
linq took 2879
with missing took 584
Pointers took 722
So it apperas that knowledge of where we have missing values (where the -1s are) is the key to it being fast.
incidentally if I loop each call to the relevant method 10000 times and check the times I get:
for loop took 190988
linq took 489052
with missing took 69198
Pointers took 159102
here the effect is even greater