Best way to copy over items to a List partially? - c#

Let's say you have two arrays, src and dest. dest is bigger. You want to copy over all the elements from src to the beginning of dest and overwrite anything that may already be there. To do this, we can do an Array.Copy(src, dest, src.Length), which is both more concise and more efficient than a for loop.
Now, say dest becomes a List<T>. What's the most efficient way to copy all of the elements from src? I know List is internally implemented using an array, so if we could get our hands on that (barring reflection, of course) we could just do an Array.Copy and this would be a non-issue.
I'm not looking for a for-loop because of said reasons, but if that's the only way to do it I guess that'll have to do.
Edit: I was hoping not to have to type up a code sample on my phone, but it seems from the barrage of incorrect answers I'm going to have to:
int[] src = { 1, 2, 3 };
var dest = new List<int>() { 4, 5, 6, 7 };
for (int i = 0; i < src.Length; i++)
{
dest[i] = src[i];
}
What I'm looking for would be the logical equivalent of the above, but have the performance of Array.Copy.

To achieve the same result as Array.Copy I would use
var src = new int[] { 50, 51, 52, 53, 54, 55 };
var dest = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
dest.RemoveRange(0, src.Length);
dest.InsertRange(0, src);
// dest: [ 50, 51, 52, 53, 54, 55, 7, 8, 9, 10, 11, 12 ]
Edit:
This method is about 7 times slower than Array.Copy, but for large Arrays much faster than to loop through the array. If you have smal source arrays, a loop may be the best choice.

I think you are looking for something like this:
int[] desta = (int[]) typeof(List<int>)
.GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(dest);
Array.Copy(src, desta, src.Length)

Why can't you just call ToList() on src array which will Create a List<int> from int[]. A small sample
int[] src = new int[] { 1,2,3,4,5,6,7,8,44,555,45,654};
List<int> dest = src.ToList();

You can also do
int[] src = new int[] {1, 2, 3};
List<int> dest = new List<int>(src);

If your src is a list, another option is to use ForEach defined in List, I believe the performance is similar to Array.Copy
dest = new List<T>();
src.ForEach(item => dest.Add(item));
If you need value copy, you can write this:
src.ForEach(item => dest.Add(item.Clone()));
In this case, just need to make sure item is ICloneable.

Just compared your function with the Array.Copy() function.
//values used for the benchmark
int[] src = { 1, 2, 3 };
int[] destArray = { 4, 5, 6, 7 };
var destList = new List<int>() { 4, 5, 6, 7 };
//Array.Copy() test : avarage 1004 ms
for (int i = 0; i < 20000000; i++)
{
Array.Copy(src, destArray, src.Length);
}
//Your solution test : avarage 634 ms
for (int i = 0; i < 20000000; i++)
{
Copy(src, destList, src.Length);
}
public void Copy(int[] sourceArray, List<int> destinationList, int length)
{
for (int i = 0; i < length; i++)
{
destinationList[i] = sourceArray[i];
}
}
*these results are the avarage of 20 benchmarks.

Related

Take n elements. If at end start from begining

How can I take n elements from a m elements collection so that if I run out of elements it starts from the beginning?
List<int> list = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
List<int> newList = list.Skip(9).Take(2).ToList();
List<int> expected = new List(){10,1};
CollectionAssert.AreEqual(expected, newList);
How can I get the expected list?
I'm looking for a CircularTake() function or something in that direction.
use an extension method to circular repeat the enumerable
public static IEnumerable<T> Circular<T>( this IEnumerable<T> source )
{
while (true)
foreach (var item in source)
yield return item;
}
and you can use your code
List<int> list = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
List<int> newList = list.Circular().Skip(9).Take(2).ToList();
.net fiddle example
You don't need to track the overflow because we can use the % modulus operator (which returns the remainder from an integer division) to continually loop through a range of indexes, and it will always return a valid index in the collection, wrapping back to 0 when it gets to the end (and this will work for multiple wraps around the end of the list):
List<int> list = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
List<int> newList = new List<int>();
for (int skip = 9, take = 2; take > 0; skip++, take--)
{
newList.Add(list[skip % list.Count]);
}
Result:
// newList == { 10, 1 }
This could be extracted into an extension method:
public static List<T> SkipTakeWrap<T>(this List<T> source, int skip, int take)
{
var newList = new List<T>();
while (take > 0)
{
newList.Add(source[skip % source.Count]);
skip++;
take--;
}
return newList;
}
And then it could be called like:
List<int> list = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> newList = list.SkipTakeWrap(9, 2);
you may need to do something like this
var start = 9;
var amount = 2;
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<int> listOverflow = list.ToList();
var overflow = (start + amount) - list.Count;
if (overflow > 0)
for (var i = 0; i < overflow; i++)
listOverflow.AddRange(list.ToList());
var newList = listOverflow.Skip(start).Take(amount).ToList();
My take on a CircularTake extension.
public static IEnumerable<T> CircularTake<T>(this IReadOnlyList<T> source, int count)
{
return Enumerable.Range(0, count).Select(i => source[i % source.Count]);
}
int overflow = take - (elements.Count - skip);
if(overflow > 0)
{
results.AddRange(elements.Skip(skip).Take(take - overflow));
results.AddRange(elements.Take(overflow));
}
If there is a possiblity that there are more than one circular iterations, e.g. from 3 elements, take 10 or more, then you can apply this logic in a recursive function.

Check if all elements from one array are in the second array c#

How can I check if all the elements from one array are in another array? I have a 2d array with 3 arrays in it,and I want to check all of those 3 arrays if they have all the elements from allnumbers. array1=allnumbers ? array2=allnumbers ? array1=allnumber2 ? I need to return true if at least one contains all the elements from allnumbers. I have the code bellow,but I need it not to contain more than 3 control flow statements.
// int[,][] array = {array1, array2, array3}
static bool CheckLine(int[,][] array)
{
const int maxL = 9;
bool result = false;
int[] allnumbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach (var singlearray in array)
{
int[] arr = singlearray;
int p = 0;
foreach (var num in allnumbers)
{
foreach (var arraynumber in singlearray)
{
if (arraynumber == num)
{
p++;
}
}
if (p == maxL)
{
result = true;
break;
}
}
}
return result;
}
If the values in your array are unique, and you don't care about the order they're in, this is a job for HashSet. (In other words, if your arrays contain sets of numbers you can treat them as sets.) Here's the basic outline of comparing sets.
var allnumbersSet = new HashSet<int>(allnumbers);
var allnumbers2Set= new HashSet<int>(allnumbers2);
if (allnumbersSet.IsSupersetOf(allnumbers2Set)) {
/* everything in allnumbers2 is also in allnumbers1 */
}
The people who put together DotNet did a really good job creating and optimizing those collection classes; you can use them with confidence to get good performance.
It seems, that you have two-dimensional jagged array. You can simplify your code by using Except and check the difference between allnumbers array and single row at every loop iteration.
static bool CheckLine(int[,][] array)
{
int[] allnumbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach (var singlearray in array)
{
var diff = allnumbers.Except(singlearray);
if (!diff.Any())
{
return true;
}
}
return false;
}
If there is no elements in a difference, it'll mean that single item from source 2D array has all elements from allnumbers array.
Example of the usage
var array = new int[2, 2][];
array[0, 0] = new[] { 1, 2, 8 };
array[0, 1] = new[] { 3, 4, 5, 6 };
array[1, 1] = new[] { 3, 2, 1, 4, 5, 7, 6, 10, 9, 8 };
array[1, 0] = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
CheckLine(array);
The last two items satisfy the condition, execution will break and return true for { 3, 2, 1, 4, 5, 7, 6, 10, 9, 8 } array. Also don't forget to add using System.Linq directive at the top of file
Thank you for your help. I forgot to mention that I can use only "using System;"

C# - sorting a rectangular array by selected columns

Please how do I sort the following array (in C#) by the second column desc, then by the first column asc (and then insert the resulting rank in the third column which I guess might be a separate question).
I'm a beginner in C#, I have spent quite some time looking for an answer to this seemingly simple question, but I couldn't find any approach that would work for me. Any help is much appreciated, thanks.
int[,] myArray = new int[5, 4]
{
{1, 7, 0, 0} ,
{2, 12, 0, 0} ,
{3, 15, 0, 0} ,
{4, 7, 0, 0} ,
{5, 1, 0, 0}
};
In a multidimensional array, there isn’t really a concept of “rows”. All values are just in the same relation to another. That’s why sorting multidimensional arrays is somewhat tricky.
It gets easier when you use jagged arrays, basically arrays of arrays. Your array would look like this:
int[][] myJaggedArray = new int[][]
{
new int[] { 1, 7, 0, 0 },
new int[] { 2, 12, 0, 0 },
new int[] { 3, 15, 0, 0 },
new int[] { 4, 7, 0, 0 },
new int[] { 5, 1, 0, 0 },
};
You can also convert your multidimensional array into a jagged array, doing something like this:
int[][] myJaggedArray = new int[myArray.GetUpperBound(0) + 1][];
for(int i = 0; i < myJaggedArray.Length; i++)
myJaggedArray[i] = Enumerable.Range(0, myArray.GetUpperBound(1) + 1).Select(k => myArray[i,k]).ToArray();
Once you have a jagged array, sorting it is really easy using LINQ:
var result = myJaggedArray
.OrderBy(row => row[1])
.ThenBy(row => row[0])
.Select((row, idx) =>
{
row[2] = idx;
return row;
})
.ToArray();

How to find a range matching certain conditions in a list using LINQ

This may be a bit difficult to describe, but I am building some statistical models and I need help with Linq syntax... My analogy is not exactly what I'm doing but it is the simplest description I can come up with...
I have an "ordered" list of inputs... Let's assume that I have an indeterminate list of Int's within a range... This list could contain anywhere from 1 to several million entities...
List<int> = [1, 3, 15, 16, 4, 27, 65, 2, 99, 3, 16, 21, 72, 1, 5, 7, 2, 8... ] (range 1 - 100).
What I am looking to extrapolate is "all" the ranges that contain a specific "search" set (or sub list) of entities. Each sub-list must contain "all" the entities from within the original input list, that is to maintain the inner erroneous data, and must not change the order... for instance, if I wanted to search for "all" ranges that contain [1, 2, 3, 4] from the list above I should get
[1, 3, 15, 16, 4, 27, 65, 2] - the first sub-list that contains the union of the search list.
[3, 15, 16, 4, 27, 65, 2, 99, 3, 16, 21, 72, 1] - next list...
[4, 27, 65, 2, 99, 3, 16, 21, 72, 1] - next list... etc...
The real critical piece of information is the "starting and ending indices" of each list... This data will need to be stored for use in a Neural Network as vector data... With this data the NN could simply use the index object to do the critical data calculations...
After some evaluation I realized that obviously each list will start and end with a search entity. This led me to start with this...
var indicies = lc.IntListData
.Select((v, i) => new { value = v, index = i })
.Where(n => search.Contains(n.value))
.ToList();
This reduced my list extensively, from looking at lists of millions of values to looking at thousands of anonymous types of value and index... Now, what I believe I need, is to find from "indicies", the first "n" anonymous types until I have at least one of each "value" in the list... No? Then simply use the min and max of the index values as necessary...
Any help with the Linq syntax to accomplish this would be most helpful.
LINQ version (with helper extension method):
If you're willing to accept a helper function to create tuples from your list of potential matches, you can get the LINQ down to this:
var subranges = matches
.Tuples(search.Length)
.Where(t => t.Select(n => n.Value).Distinct().Count() == search.Length)
.Select(t => new { StartIndex=t.Min(n => n.Index), EndIndex=t.Max(n => n.Index) })
.Select(r => list.Skip(r.StartIndex).Take(r.EndIndex-r.StartIndex+1))
;
The Tuples method is a variation of the extension method from this answer: https://stackoverflow.com/a/577612/209103.
public static IEnumerable<IEnumerable<T>> Tuples<T>(this IEnumerable<T> sequence, int nTuple)
{
if(nTuple <= 0) throw new ArgumentOutOfRangeException("nTuple");
for(int i = 0; i <= sequence.Count() - nTuple; i++)
for (int j = i+nTuple; j < sequence.Count(); j++)
yield return sequence.Skip(i).Take(j-i);
}
Note that Tuples is O(nn) and the entire solution is O(nn*n), so will not perform well for larger data sets.
Non LINQ versions below
Rough first draft of a non-LINQ version:
var list = new [] { 1, 3, 15, 16, 4, 27, 65, 2, 99, 3, 16, 21, 72, 1, 5, 7, 2, 8 };
var search = new [] { 1, 2, 3, 4 };
var group = new List<int>();
var start = -1;
for (var i=0; i < list.Length; i++) {
if (search.Contains(list[i])) {
if (!group.Any()) {
start = i;
}
if (!group.Contains(list[i])) {
group.Add(list[i]);
}
if (group.Count == search.Length) {
Console.WriteLine(start+" - "+i);
group.Clear();
i = start + 1;
start = -1;
}
}
}
This uses brute force, but could be optimized with your method of finding matching indices first.
Update - optimized by only considering relevant indices
var list = new [] { 1, 3, 15, 16, 4, 27, 65, 2, 1, 99, 3, 16, 21, 72, 1, 5, 7, 4, 2, 8 };
var search = new [] { 1, 2, 3, 4 };
var matches = list
.Select((v,i) => new { Value=v, Index=i })
.Where(n => search.Contains(n.Value))
.ToList()
;
for (var start=0; start < matches.Count(); start++) {
var group = new List<int>();
group.Add(matches[start].Value);
foreach (var match in matches.Skip(start)) {
if (!group.Contains(match.Value)) {
group.Add(match.Value);
}
if (group.Count == search.Length) {
Console.WriteLine(matches[start].Index+" - "+match.Index);
break;
}
}
}
Both approaches are non-LINQ. I'm not sure how to go about turning this into a LINQ expression. It's clearly a grouping exercise (.GroupBy), but I can't see what expression to group on.

What's the best way to apply a "Join" method generically similar to how String.Join(...) works?

If I have a string array, for example: var array = new[] { "the", "cat", "in", "the", "hat" }, and I want to join them with a space between each word I can simply call String.Join(" ", array).
But, say I had an array of integer arrays (just like I can have an array of character arrays). I want to combine them into one large array (flatten them), but at the same time insert a value between each array.
var arrays = new[] { new[] { 1, 2, 3 }, new[] { 4, 5, 6 }, new { 7, 8, 9 }};
var result = SomeJoin(0, arrays); // result = { 1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9 }
I wrote something up, but it is very ugly, and I'm sure that there is a better, cleaner way. Maybe more efficient?
var result = new int[arrays.Sum(a => a.Length) + arrays.Length - 1];
int offset = 0;
foreach (var array in arrays)
{
Buffer.BlockCopy(array, 0, result, offset, b.Length);
offset += array.Length;
if (offset < result.Length)
{
result[offset++] = 0;
}
}
Perhaps this is the most efficient? I don't know... just seeing if there is a better way. I thought maybe LINQ would solve this, but sadly I don't see anything that is what I need.
You can generically "join" sequences via:
public static IEnumerable<T> Join<T>(T separator, IEnumerable<IEnumerable<T>> items)
{
var sep = new[] {item};
var first = items.FirstOrDefault();
if (first == null)
return Enumerable.Empty<T>();
else
return first.Concat(items.Skip(1).SelectMany(i => sep.Concat(i)));
}
This works with your code:
var arrays = new[] { new[] { 1, 2, 3 }, new[] { 4, 5, 6 }, new { 7, 8, 9 }};
var result = Join(0, arrays); // result = { 1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9 }
The advantage here is that this will work with any IEnumerable<IEnumerable<T>>, and isn't restricted to lists or arrays. Note that this will insert a separate in between two empty sequences, but that behavior could be modified if desired.
public T[] SomeJoin<T>(T a, T[][] arrays){
return arrays.SelectMany((x,i)=> i == arrays.Length-1 ? x : x.Concat(new[]{a}))
.ToArray();
}
NOTE: The code works seamlessly because of using Array, otherwise we may lose some performance cost to get the Count of the input collection.
This may not be the most efficient, but it is quite extensible:
public static IEnumerable<T> Join<T>(this IEnumerable<IEnumerable<T>> source, T separator)
{
bool firstTime = true;
foreach (var collection in source)
{
if (!firstTime)
yield return separator;
foreach (var value in collection)
yield return value;
firstTime = false;
}
}
...
var arrays = new[] { new[] { 1, 2, 3 }, new[] { 4, 5, 6 }, new[] { 7, 8, 9 }};
var result = arrays.Join(0).ToArray();
// result = { 1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9 }

Categories