Adding values to a C# array - c#
Probably a really simple one this - I'm starting out with C# and need to add values to an array, for example:
int[] terms;
for(int runs = 0; runs < 400; runs++)
{
terms[] = runs;
}
For those who have used PHP, here's what I'm trying to do in C#:
$arr = array();
for ($i = 0; $i < 10; $i++) {
$arr[] = $i;
}
You can do this way -
int[] terms = new int[400];
for (int runs = 0; runs < 400; runs++)
{
terms[runs] = value;
}
Alternatively, you can use Lists - the advantage with lists being, you don't need to know the array size when instantiating the list.
List<int> termsList = new List<int>();
for (int runs = 0; runs < 400; runs++)
{
termsList.Add(value);
}
// You can convert it back to an array if you would like to
int[] terms = termsList.ToArray();
Edit: a) for loops on List<T> are a bit more than 2 times cheaper than foreach loops on List<T>, b) Looping on array is around 2 times cheaper than looping on List<T>, c) looping on array using for is 5 times cheaper than looping on List<T> using foreach (which most of us do).
Using Linq's method Concat makes this simple
int[] array = new int[] { 3, 4 };
array = array.Concat(new int[] { 2 }).ToArray();
result
3,4,2
If you're writing in C# 3, you can do it with a one-liner:
int[] terms = Enumerable.Range(0, 400).ToArray();
This code snippet assumes that you have a using directive for System.Linq at the top of your file.
On the other hand, if you're looking for something that can be dynamically resized, as it appears is the case for PHP (I've never actually learned it), then you may want to use a List instead of an int[]. Here's what that code would look like:
List<int> terms = Enumerable.Range(0, 400).ToList();
Note, however, that you cannot simply add a 401st element by setting terms[400] to a value. You'd instead need to call Add() like this:
terms.Add(1337);
By 2019 you can use Append, Prepend using LinQ in just one line
using System.Linq;
and then in NET 6.0:
terms = terms.Append(21);
or versions lower than NET 6.0
terms = terms.Append(21).ToArray();
Answers on how to do it using an array are provided here.
However, C# has a very handy thing called System.Collections
Collections are fancy alternatives to using an array, though many of them use an array internally.
For example, C# has a collection called List that functions very similar to the PHP array.
using System.Collections.Generic;
// Create a List, and it can only contain integers.
List<int> list = new List<int>();
for (int i = 0; i < 400; i++)
{
list.Add(i);
}
Using a List as an intermediary is the easiest way, as others have described, but since your input is an array and you don't just want to keep the data in a List, I presume you might be concerned about performance.
The most efficient method is likely allocating a new array and then using Array.Copy or Array.CopyTo. This is not hard if you just want to add an item to the end of the list:
public static T[] Add<T>(this T[] target, T item)
{
if (target == null)
{
//TODO: Return null or throw ArgumentNullException;
}
T[] result = new T[target.Length + 1];
target.CopyTo(result, 0);
result[target.Length] = item;
return result;
}
I can also post code for an Insert extension method that takes a destination index as input, if desired. It's a little more complicated and uses the static method Array.Copy 1-2 times.
Based on the answer of Thracx (I don't have enough points to answer):
public static T[] Add<T>(this T[] target, params T[] items)
{
// Validate the parameters
if (target == null) {
target = new T[] { };
}
if (items== null) {
items = new T[] { };
}
// Join the arrays
T[] result = new T[target.Length + items.Length];
target.CopyTo(result, 0);
items.CopyTo(result, target.Length);
return result;
}
This allows to add more than just one item to the array, or just pass an array as a parameter to join two arrays.
You have to allocate the array first:
int [] terms = new int[400]; // allocate an array of 400 ints
for(int runs = 0; runs < terms.Length; runs++) // Use Length property rather than the 400 magic number again
{
terms[runs] = value;
}
int ArraySize = 400;
int[] terms = new int[ArraySize];
for(int runs = 0; runs < ArraySize; runs++)
{
terms[runs] = runs;
}
That would be how I'd code it.
C# arrays are fixed length and always indexed. Go with Motti's solution:
int [] terms = new int[400];
for(int runs = 0; runs < 400; runs++)
{
terms[runs] = value;
}
Note that this array is a dense array, a contiguous block of 400 bytes where you can drop things. If you want a dynamically sized array, use a List<int>.
List<int> terms = new List<int>();
for(int runs = 0; runs < 400; runs ++)
{
terms.Add(runs);
}
Neither int[] nor List<int> is an associative array -- that would be a Dictionary<> in C#. Both arrays and lists are dense.
You can't just add an element to an array easily. You can set the element at a given position as fallen888 outlined, but I recommend to use a List<int> or a Collection<int> instead, and use ToArray() if you need it converted into an array.
If you really need an array the following is probly the simplest:
using System.Collections.Generic;
// Create a List, and it can only contain integers.
List<int> list = new List<int>();
for (int i = 0; i < 400; i++)
{
list.Add(i);
}
int [] terms = list.ToArray();
one approach is to fill an array via LINQ
if you want to fill an array with one element
you can simply write
string[] arrayToBeFilled;
arrayToBeFilled= arrayToBeFilled.Append("str").ToArray();
furthermore, If you want to fill an array with multiple elements you can use the
previous code in a loop
//the array you want to fill values in
string[] arrayToBeFilled;
//list of values that you want to fill inside an array
List<string> listToFill = new List<string> { "a1", "a2", "a3" };
//looping through list to start filling the array
foreach (string str in listToFill){
// here are the LINQ extensions
arrayToBeFilled= arrayToBeFilled.Append(str).ToArray();
}
Array Push Example
public void ArrayPush<T>(ref T[] table, object value)
{
Array.Resize(ref table, table.Length + 1); // Resizing the array for the cloned length (+-) (+1)
table.SetValue(value, table.Length - 1); // Setting the value for the new element
}
int[] terms = new int[10]; //create 10 empty index in array terms
//fill value = 400 for every index (run) in the array
//terms.Length is the total length of the array, it is equal to 10 in this case
for (int run = 0; run < terms.Length; run++)
{
terms[run] = 400;
}
//print value from each of the index
for (int run = 0; run < terms.Length; run++)
{
Console.WriteLine("Value in index {0}:\t{1}",run, terms[run]);
}
Console.ReadLine();
/*Output:
Value in index 0: 400
Value in index 1: 400
Value in index 2: 400
Value in index 3: 400
Value in index 4: 400
Value in index 5: 400
Value in index 6: 400
Value in index 7: 400
Value in index 8: 400
Value in index 9: 400
*/
If you don't know the size of the Array or already have an existing array that you are adding to. You can go about this in two ways. The first is using a generic List<T>:
To do this you will want convert the array to a var termsList = terms.ToList(); and use the Add method. Then when done use the var terms = termsList.ToArray(); method to convert back to an array.
var terms = default(int[]);
var termsList = terms == null ? new List<int>() : terms.ToList();
for(var i = 0; i < 400; i++)
termsList.Add(i);
terms = termsList.ToArray();
The second way is resizing the current array:
var terms = default(int[]);
for(var i = 0; i < 400; i++)
{
if(terms == null)
terms = new int[1];
else
Array.Resize<int>(ref terms, terms.Length + 1);
terms[terms.Length - 1] = i;
}
If you are using .NET 3.5 Array.Add(...);
Both of these will allow you to do it dynamically. If you will be adding lots of items then just use a List<T>. If it's just a couple of items then it will have better performance resizing the array. This is because you take more of a hit for creating the List<T> object.
Times in ticks:
3 items
Array Resize Time: 6
List Add Time: 16
400 items
Array Resize Time: 305
List Add Time: 20
I will add this for a another variant. I prefer this type of functional coding lines more.
Enumerable.Range(0, 400).Select(x => x).ToArray();
You can't do this directly. However, you can use Linq to do this:
List<int> termsLst=new List<int>();
for (int runs = 0; runs < 400; runs++)
{
termsLst.Add(runs);
}
int[] terms = termsLst.ToArray();
If the array terms wasn't empty in the beginning, you can convert it to List first then do your stuf. Like:
List<int> termsLst = terms.ToList();
for (int runs = 0; runs < 400; runs++)
{
termsLst.Add(runs);
}
terms = termsLst.ToArray();
Note: don't miss adding 'using System.Linq;' at the begaining of the file.
This seems like a lot less trouble to me:
var usageList = usageArray.ToList();
usageList.Add("newstuff");
usageArray = usageList.ToArray();
Just a different approach:
int runs = 0;
bool batting = true;
string scorecard;
while (batting = runs < 400)
scorecard += "!" + runs++;
return scorecard.Split("!");
int[] terms = new int[400];
for(int runs = 0; runs < 400; runs++)
{
terms[runs] = value;
}
static void Main(string[] args)
{
int[] arrayname = new int[5];/*arrayname is an array of 5 integer [5] mean in array [0],[1],[2],[3],[4],[5] because array starts with zero*/
int i, j;
/*initialize elements of array arrayname*/
for (i = 0; i < 5; i++)
{
arrayname[i] = i + 100;
}
/*output each array element value*/
for (j = 0; j < 5; j++)
{
Console.WriteLine("Element and output value [{0}]={1}",j,arrayname[j]);
}
Console.ReadKey();/*Obtains the next character or function key pressed by the user.
The pressed key is displayed in the console window.*/
}
/*arrayname is an array of 5 integer*/
int[] arrayname = new int[5];
int i, j;
/*initialize elements of array arrayname*/
for (i = 0; i < 5; i++)
{
arrayname[i] = i + 100;
}
To add the list values to string array using C# without using ToArray() method
List<string> list = new List<string>();
list.Add("one");
list.Add("two");
list.Add("three");
list.Add("four");
list.Add("five");
string[] values = new string[list.Count];//assigning the count for array
for(int i=0;i<list.Count;i++)
{
values[i] = list[i].ToString();
}
Output of the value array contains:
one
two
three
four
five
You can do this is with a list. here is how
List<string> info = new List<string>();
info.Add("finally worked");
and if you need to return this array do
return info.ToArray();
Here is one way how to deal with adding new numbers and strings to Array:
int[] ids = new int[10];
ids[0] = 1;
string[] names = new string[10];
do
{
for (int i = 0; i < names.Length; i++)
{
Console.WriteLine("Enter Name");
names[i] = Convert.ToString(Console.ReadLine());
Console.WriteLine($"The Name is: {names[i]}");
Console.WriteLine($"the index of name is: {i}");
Console.WriteLine("Enter ID");
ids[i] = Convert.ToInt32(Console.ReadLine());
Console.WriteLine($"The number is: {ids[i]}");
Console.WriteLine($"the index is: {i}");
}
} while (names.Length <= 10);
Related
Is there a way to make the Loop Code make it faster?
For Loop Code int counts = 0; List<int> count = new List<int>(); List<int> goodnumber = new List<int>(); for (int i = lower; i <= upper; i++) { if (!badNumbers.Contains(i)) { goodnumber.Add(i); } else { count.Add(goodnumber.Count); goodnumber = new List<int>(); } if (i == upper) { count.Add(goodnumber.Count); counts = count.Max(); } } return counts; is there a way to optimize my code above? because the running time for the code above is exceeding in 3 secs. how can I make it 2 or below?
There's a few improvements you can make. badNumbers should probably be a HashSet<int> which will provide you close to O(1) lookup. You don't actually care about storing the "good numbers" (you don't use that data), so it would be more efficient to just store how many good numbers you encounter. Now you just want the max streak size (i.e. max number of consecutive good numbers) you encounter, and you can use Math.Max to compare the last "good" count with the current "good" count and choose the largest. The code looks like this: HashSet<int> badNumbers = new HashSet<int>() { 5, 4, 2, 15 }; int counts = 0; int goodNumberCount = 0; for (int i = lower; i <= upper; i++) { if (!badNumbers.Contains(i)) { ++goodNumberCount; } else { counts = Math.Max(counts, goodNumberCount); goodNumberCount = 0; } } counts = Math.Max(counts, goodNumberCount); return counts;
Call List.Clear() instead of creating new List inside the loop Call count.Max() outside the loop Remove the last if and add this line after the loop count.Add(goodnumber.Count) int counts = 0; List<int> count = new List<int>(); List<int> goodnumber = new List<int>(); for (int i = lower; i <= upper; i++) { if (!badNumbers.Contains(i)) { goodnumber.Add(i); } else { count.Add(goodnumber.Count); goodnumber.Clear(); } } count.Add(goodnumber.Count); counts = count.Max(); return counts; BTW, I don't know what are you trying to achieve with this code.
The correct way to "optimize" your code is to rewrite it. You need to think differently. The problem you have has various different solutions and you are complicating it too much. You don't need to process the input in one long cycle only. You can pre-process the list somehow, in a way, that would help you. For example sort it. Another thing that could help you is to have a variable (or variables) in which you are storing some intermediate result. For example running max, min, sum, or previous value of something Think about how you could solve the problem mathematically. Isn't it just the difference of numbers you are trying to find? You could sort the list, calculate the difference between each element, bound it by your lower and upper borders. You can either update the running maximum difference during the loop or find the maximum difference from the list of differences.
Here is a general solution: using System.Collections.Generic; using System.Linq; namespace ConsoleApp1 { class Program { static void Main(string[] args) { var lower = 1; var upper = 10; var elementCount = upper - lower + 1; var numbers = Enumerable.Range(1, elementCount); var badNumbers = new HashSet<int> { 5, 4, 2, 15 }; var maxCount = CalculateCounts(numbers, badNumbers).Max(); } private static IEnumerable<int> CalculateCounts<T>(IEnumerable<T> items, ISet<T> splitOn) { var count = 0; foreach (var item in items) { if (!splitOn.Contains(item)) count++; else { yield return count; count = 0; } } yield return count; } } }
C# Faster way to filter for loop with array of int as index?
Sorry if this is a duplicate, first question here... I wanna operate on a large array of structs called notes. But I don't wanna operate on every element of notes. I'm trying to use a filter of an int array (int[]) as to skip quite a few of it as shown in below code. Note[] notes = new Note[] { // Struct stuff ... }; int[] filter = new int[]{ 4,20,50,367... }; for (int i = 0; i < notes.Length; i++) { bool flag = false; for (int j = 0; j < filter.Length; j++) { if (i == filter[j]) { flag = true; break; } } if (flag) continue; // Do something on notes[i] } The problem is, the code will run really slow (I think) when both notes array and filter array expands. So, is there a better and faster way to do this? Note that the size of filter can be anything based on other conditions
We can get rid of inner loop with a help of HashSet<int> while having a better O(|filter| + |notes|) time complexity instead of initial O(|filter| * |notes|): Note[] notes = new Note[] { ... //Struct stuff }; int[] filter = new int[] { 4, 20, 50, 367... }; HashSet<int> toExclude = new HashSet<int>(filter); for (int i = 0; i < notes.Length; i++) { if (toExclude.Contains(i)) // O(1) time complexity continue; //Do something on notes[i] }
You could filter the notes using Linq like this: Note[] notes = new Note[]{ ...//Struct stuff }; int[] filter = new int[]{ 4,20,50,367... }; var filteredNotes = notes.ToList().Where(note => !filter.Contains(note.Id)).ToList(); foreach(var note in filteredNotes) { //Do something on note } You would need to test the performance though, as Linq tends to be slow in specific circumstances.
You can loop the filter array and create a new boolean array that has all elements you want to skip as true. bool[] filterArray= new bool[notes.Length]; foreach(var index in filter) { if(index<filterArray.Length) filterArray[index]=true; } Then you have to just check the index of this array. for (int i = 0; i < notes.Length; i++) { if(!filterArray[i]){ //Do something on notes[i] } } The complexity of this code will be O(m+n*X) where m is the length of the filter array, n the length of the node array and X the complexity of your operation on notes[i]. Assuming mO(n*X). Your complexity now is O(m*n*X)
get random 8 element values from array string without duplicated element
I have a string array with length is 100. I want to get random 8 elements among 100 element without duplicated elements in C#. Please help me. I appreciate it. I just use: for (int i = 0; i < 8; i++) { work with 8 values here } Above code just performs to get 8 first values not 8 random values.
Easy way: var random = new Random(); var randomValues = arr.OrderBy(x => random.Next()).Take(8) Effective way: use Fisher–Yates shuffle. Jon Skeet provided the implementation here.
Here is the code. Explanation Randomly generate numbers between 0 - 99. Using a hashset to keep track of indices that I am picking. If the index is present in hashset then it means that the value has already been selected, so skip it. If the values in array are not unique, then in hashset instead of tracking index track values. public List<string> GetRandomElements(string[] givenArray) { if(givenArray == null || givenArray.Length == 0) { throw new ArgumentNullException("givenArray"); } var rand = new Random(); var randomArray = new List<string>(); var indexTracker = new HashSet<int>(); while(randomArray.Count < 8) { var nextIndex = rand.Next(0, givenArray.Length); if(indexTracker.Contains(nextIndex)) { continue; } randomArray.Add(givenArray[nextIndex]); indexTracker.Add(nextIndex); } return randomArray; } Note I assumed here there was no constraint on using extra memory for hashset (because there will be max of 8 members only). If that is the constraint, then another way could be, split the index into 8 parts. So pick first index between 0-8, next index between 9-16 and so one. This way you will get unique numbers.
You can use generics and simply do: static T[] GetRandomRange<T>(T[] arr, int length) { var r = new Random(); List<T> elementsList = new List<T>(); for (; elementsList.Count < length; ) { T el = arr[r.Next(0, arr.Length)]; if (!elementsList.Contains(el)) elementsList.Add(el); } return elementsList.ToArray(); } Use it like : int[] arr ... int[] newArray = GetRandomRange(arr, 8); MyClass[] arr2... MyClass[] newArray2 = GetRandomRange(arr2, 5);
It's pretty straightforward, just pass it to HashSet<string> and apply the following logic: var mySet = new HashSet<string>(myList); var myList = new List<string>(); var random = new Random(); for (int i = 0; i < 8; i++) { int randValue = random.Next(0, mySet.Count()); string randSetValue = mySet.ElementAt(randValue); myList.Add(randSetValue ); mySet.Remove(randSetValue); }
passing arrays in c#
Hi i am working on Grade Calculation. My problem here is if the length of string array is longer that int array it works skipping the last 2 grades. ex: int[] unit = new int[] {1,-3,3,4}; string[] letter_grade = new string[] {"A", "B","B","W","D","F"}; but if length of int array longer than that of string array its not working its throwing error Index was outside the bounds of the array. int[] unit = new int[] {1,-3,3,4,5,6,7}; string[] letter_grade = new string[] {"A", "B","B"}; so my question how do i make it work for both?? int length = unit.Length; int no_units = length; double totalGrade_Points = 0.0; int totalno_units = 0; totalGPA = 0; for (int i = 0; i < unit.Length; i++) { entrygot = findGpaListentry(letter_grade[i]); //Index was outside the bounds of the array. if (entrygot != null) { //some code calculation } }
For array indexing you must have starting and stopping condition defined very well. For accessing two arrays either they must be equal or they are compared under certain valid conditions. Have a look at this: for(int i=0;i<unit.length;i++){ entrygot = findGpaListentry(letter_grade[i]);// only if letter_grade is valid under all values of i i.e unit.length } // either you have to check as if; if(lenght_of_letter_grade < i-1) //then access entrygot = findGpaListentry(letter_grade[i]);
You can't just check if the array item is null, because you would be out of the bounds of the array and you will get an exception before the null check occurs. I would check the length of the array on each iteration... for (int i = 0; i < unit.Length; i++) { if (currentArray.Length < i - 1) { break; } // other code... }
I think in your case, the number of elements will never be large hence performance wont be an issue. So I think you should be using a List instead of an array. With an array you will have to be insert checks each time some logic changes or you add other functionalities.
foreach loop is best for your scenerio. foreach (string s in letter_grade) { entrygot = findGpaListentry(s); if (entrygot != null) { //some code calculation } }
in C#, how do I order items in a list where the "largest" values are in the middle of the list
I have been stumped on this one for a while. I want to take a List and order the list such that the Products with the largest Price end up in the middle of the list. And I also want to do the opposite, i.e. make sure that the items with the largest price end up on the outer boundaries of the list. Imagine a data structure like this.. 1,2,3,4,5,6,7,8,9,10 In the first scenario I need to get back 1,3,5,7,9,10,8,6,4,2 In the second scenario I need to get back 10,8,6,4,2,1,3,5,7,9 The list may have upwards of 250 items, the numbers will not be evenly distributed, and they will not be sequential, and I wanted to minimize copying. The numbers will be contained in Product objects, and not simple primitive integers. Is there a simple solution that I am not seeing? Any thoughts. So for those of you wondering what I am up to, I am ordering items based on calculated font size. Here is the code that I went with... The Implementation... private void Reorder() { var tempList = new LinkedList<DisplayTag>(); bool even = true; foreach (var tag in this) { if (even) tempList.AddLast(tag); else tempList.AddFirst(tag); even = !even; } this.Clear(); this.AddRange(tempList); } The Test... [TestCase(DisplayTagOrder.SmallestToLargest, Result=new[]{10,14,18,22,26,30})] [TestCase(DisplayTagOrder.LargestToSmallest, Result=new[]{30,26,22,18,14,10})] [TestCase(DisplayTagOrder.LargestInTheMiddle, Result = new[] { 10, 18, 26, 30, 22, 14 })] [TestCase(DisplayTagOrder.LargestOnTheEnds, Result = new[] { 30, 22, 14, 10, 18, 26 })] public int[] CalculateFontSize_Orders_Tags_Appropriately(DisplayTagOrder sortOrder) { list.CloudOrder = sortOrder; list.CalculateFontSize(); var result = (from displayTag in list select displayTag.FontSize).ToArray(); return result; } The Usage... public void CalculateFontSize() { GetMaximumRange(); GetMinimunRange(); CalculateDelta(); this.ForEach((displayTag) => CalculateFontSize(displayTag)); OrderByFontSize(); } private void OrderByFontSize() { switch (CloudOrder) { case DisplayTagOrder.SmallestToLargest: this.Sort((arg1, arg2) => arg1.FontSize.CompareTo(arg2.FontSize)); break; case DisplayTagOrder.LargestToSmallest: this.Sort(new LargestFirstComparer()); break; case DisplayTagOrder.LargestInTheMiddle: this.Sort(new LargestFirstComparer()); Reorder(); break; case DisplayTagOrder.LargestOnTheEnds: this.Sort(); Reorder(); break; } }
The appropriate data structure is a LinkedList because it allows you to efficiently add to either end: LinkedList<int> result = new LinkedList<int>(); int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; Array.Sort(array); bool odd = true; foreach (var x in array) { if (odd) result.AddLast(x); else result.AddFirst(x); odd = !odd; } foreach (int item in result) Console.Write("{0} ", item); No extra copying steps, no reversing steps, ... just a small overhead per node for storage.
C# Iterator version (Very simple code to satisfy all conditions.) One function to rule them all! Doesn't use intermediate storage collection (see yield keyword). Orders the large numbers either to the middle, or to the sides depending on the argument. It's implemented as a C# iterator // Pass forward sorted array for large middle numbers, // or reverse sorted array for large side numbers. // public static IEnumerable<long> CurveOrder(long[] nums) { if (nums == null || nums.Length == 0) yield break; // Nothing to do. // Move forward every two. for (int i = 0; i < nums.Length; i+=2) yield return nums[i]; // Move backward every other two. Note: Length%2 makes sure we're on the correct offset. for (int i = nums.Length-1 - nums.Length%2; i >= 0; i-=2) yield return nums[i]; } Example Usage For example with array long[] nums = { 1,2,3,4,5,6,7,8,9,10,11 }; Start with forward sort order, to bump high numbers into the middle. Array.Sort(nums); //forward sort // Array argument will be: { 1,2,3,4,5,6,7,8,9,10,11 }; long[] arrLargeMiddle = CurveOrder(nums).ToArray(); Produces: 1 3 5 7 9 11 10 8 6 4 2 Or, Start with reverse sort order, to push high numbers to sides. Array.Reverse(nums); //reverse sort // Array argument will be: { 11,10,9,8,7,6,5,4,3,2,1 }; long[] arrLargeSides = CurveOrder(nums).ToArray(); Produces: 11 9 7 5 3 1 2 4 6 8 10 Significant namespaces are: using System; using System.Collections.Generic; using System.Linq; Note: The iterator leaves the decision up to the caller about whether or not to use intermediate storage. The caller might simply be issuing a foreach loop over the results instead. Extension Method Option Optionally change the static method header to use the this modifier public static IEnumerable<long> CurveOrder(this long[] nums) { and put it inside a static class in your namespace; Then call the order method directly on any long[ ] array instance like so: Array.Reverse(nums); //reverse sort // Array argument will be: { 11,10,9,8,7,6,5,4,3,2,1 }; long[] arrLargeSides = nums.CurveOrder().ToArray(); Just some (unneeded) syntactic sugar to mix things up a bit for fun. This can be applied to any answers to your question that take an array argument.
I might go for something like this static T[] SortFromMiddleOut<T, U>(IList<T> list, Func<T, U> orderSelector, bool largestInside) where U : IComparable<U> { T[] sortedArray = new T[list.Count]; bool add = false; int index = (list.Count / 2); int iterations = 0; IOrderedEnumerable<T> orderedList; if (largestInside) orderedList = list.OrderByDescending(orderSelector); else orderedList = list.OrderBy(orderSelector); foreach (T item in orderedList) { sortedArray[index] = item; if (add) index += ++iterations; else index -= ++iterations; add = !add; } return sortedArray; } Sample invocations: int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int[] sortedArray = SortFromMiddleOut(array, i => i, false); foreach (int item in sortedArray) Console.Write("{0} ", item); Console.Write("\n"); sortedArray = SortFromMiddleOut(array, i => i, true); foreach (int item in sortedArray) Console.Write("{0} ", item); With it being generic, it could be a list of Foo and the order selector could be f => f.Name or whatever you want to throw at it.
The fastest (but not the clearest) solution is probably to simply calculate the new index for each element: Array.Sort(array); int length = array.Length; int middle = length / 2; int[] result2 = new int[length]; for (int i = 0; i < array.Length; i++) { result2[middle + (1 - 2 * (i % 2)) * ((i + 1) / 2)] = array[i]; }
Something like this? public IEnumerable<int> SortToMiddle(IEnumerable<int> input) { var sorted = new List<int>(input); sorted.Sort(); var firstHalf = new List<int>(); var secondHalf = new List<int>(); var sendToFirst = true; foreach (var current in sorted) { if (sendToFirst) { firstHalf.Add(current); } else { secondHalf.Add(current); } sendToFirst = !sendToFirst; } //to get the highest values on the outside just reverse //the first list instead of the second secondHalf.Reverse(); return firstHalf.Concat(secondHalf); } For your specific (general) case (assuming unique keys): public static IEnumerable<T> SortToMiddle<T, TU>(IEnumerable<T> input, Func<T, TU> getSortKey) { var sorted = new List<TU>(input.Select(getSortKey)); sorted.Sort(); var firstHalf = new List<TU>(); var secondHalf = new List<TU>(); var sendToFirst = true; foreach (var current in sorted) { if (sendToFirst) { firstHalf.Add(current); } else { secondHalf.Add(current); } sendToFirst = !sendToFirst; } //to get the highest values on the outside just reverse //the first list instead of the second secondHalf.Reverse(); sorted = new List<TU>(firstHalf.Concat(secondHalf)); //This assumes the sort keys are unique - if not, the implementation //needs to use a SortedList<TU, T> return sorted.Select(s => input.First(t => s.Equals(getSortKey(t)))); } And assuming non-unique keys: public static IEnumerable<T> SortToMiddle<T, TU>(IEnumerable<T> input, Func<T, TU> getSortKey) { var sendToFirst = true; var sorted = new SortedList<TU, T>(input.ToDictionary(getSortKey, t => t)); var firstHalf = new SortedList<TU, T>(); var secondHalf = new SortedList<TU, T>(); foreach (var current in sorted) { if (sendToFirst) { firstHalf.Add(current.Key, current.Value); } else { secondHalf.Add(current.Key, current.Value); } sendToFirst = !sendToFirst; } //to get the highest values on the outside just reverse //the first list instead of the second secondHalf.Reverse(); return(firstHalf.Concat(secondHalf)).Select(kvp => kvp.Value); }
Simplest solution - order the list descending, create two new lists, into the first place every odd-indexed item, into the other every even indexed item. Reverse the first list then append the second to the first.
Okay, I'm not going to question your sanity here since I'm sure you wouldn't be asking the question if there weren't a good reason :-) Here's how I'd approach it. Create a sorted list, then simply create another list by processing the keys in order, alternately inserting before and appending, something like: sortedlist = list.sort (descending) biginmiddle = new list() state = append foreach item in sortedlist: if state == append: biginmiddle.append (item) state = prepend else: biginmiddle.insert (0, item) state = append This will give you a list where the big items are in the middle. Other items will fan out from the middle (in alternating directions) as needed: 1, 3, 5, 7, 9, 10, 8, 6, 4, 2 To get a list where the larger elements are at the ends, just replace the initial sort with an ascending one. The sorted and final lists can just be pointers to the actual items (since you state they're not simple integers) - this will minimise both extra storage requirements and copying.
Maybe its not the best solution, but here's a nifty way... Let Product[] parr be your array. Disclaimer It's java, my C# is rusty. Untested code, but you get the idea. int plen = parr.length int [] indices = new int[plen]; for(int i = 0; i < (plen/2); i ++) indices[i] = 2*i + 1; // Line1 for(int i = (plen/2); i < plen; i++) indices[i] = 2*(plen-i); // Line2 for(int i = 0; i < plen; i++) { if(i != indices[i]) swap(parr[i], parr[indices[i]]); } The second case, Something like this? int plen = parr.length int [] indices = new int[plen]; for(int i = 0; i <= (plen/2); i ++) indices[i] = (plen^1) - 2*i; for(int i = 0; i < (plen/2); i++) indices[i+(plen/2)+1] = 2*i + 1; for(int i = 0; i < plen; i++) { if(i != indices[i]) swap(parr[i], parr[indices[i]]); }