How to Know This is the Last Iteration of a Loop? - c#

Is there a fancy way to know you are in the last loop in a List without using counters
List<string> myList = new List<string>() {"are", "we", "there", "yet"};
foreach(string myString in myList) {
// Is there a fancy way to find out here if this is the last time through?
}

No, you will have to use a regular for(int i=0; i<myList.Length; i++) { ... } loop for that.

How about using a for loop instead?
List<string> myList = new List<string>() {"are", "we", "there", "yet"};
for (var i=0; i<myList.Count; i++)
{
var myString = myList[i];
if (i==myList.Count-1)
{
// this is the last item in the list
}
}
foreach is useful - but if you need to keep count of what you are doing, or index things by count, then just revert to a good old for loop.

There are two ways I would do it.
First, with a for loop instead of foreach
for(int i = 0; i < myList.Count; i++)
{
string myString = myList[i];
bool isLast = i == myList.Count - 1;
...
}
Or, if this needs to work with enumerators, change the order of things. Normally MoveNext is done as the control of the while loop, but if we do it right at the beginning of the loop, we can use its return to determine if we're at the end of the list or not.
IEnumerator<string> enumerator = myList.GetEnumerator();
bool isLast = !enumerator.MoveNext();
while(!isLast)
{
string myString = enumerator.Current;
isLast = !enumerator.MoveNext();
...
}

There's no efficient way to do this.
Just use a for loop and index. It runs faster any way.

Well...there is no way to know that. Not as easy as you might guess. The list data structure is just a way to order items one after another. But you don't have a way to say if a item is the last item just using foreach structure. Best use for structure with the count property to know if you hit the last (or previous) item.
var length = list.Count;
for (var idx = 0; idx < list.Count; idx++)
{
if (idx == (length - 1))
{
// The last item. Do something
}
}
hope it helps.

No, there isn't, you'll have to use a counter if you want to use foreach.

Use for operator instead of foreach

The "fancy" way is to maintain 1 element of lookahead, but why on earth would you want to? Jus maintain a count. Here's the fancy way. No counters, no checking the Count property. All it uses is an enumerator:
using System;
using System.Collections.Generic;
namespace Sandbox
{
class Program
{
enum ListPosition : byte
{
First = 0x01 ,
Only = First|Last ,
Middle = 0x02 ,
Last = 0x04 ,
Exhausted = 0x00 ,
}
private static void WalkList( List<int> numbers )
{
List<int>.Enumerator numberWalker = numbers.GetEnumerator();
bool currFetched = numberWalker.MoveNext();
int currValue = currFetched ? numberWalker.Current : default( int );
bool nextFetched = numberWalker.MoveNext();
int nextValue = nextFetched ? numberWalker.Current : default( int );
ListPosition position ;
if ( currFetched && nextFetched ) position = ListPosition.First ;
else if ( currFetched && ! nextFetched ) position = ListPosition.Only ;
else if ( ! currFetched ) position = ListPosition.Exhausted ;
else throw new InvalidOperationException( "Reached Unreachable Code. Hmmm...that doesn't seem quite right" );
while ( position != ListPosition.Exhausted )
{
string article = ( position==ListPosition.Middle?"a":"the" );
Console.WriteLine( " {0} is {1} {2} item in the list" , currValue , article , position );
currFetched = nextFetched ;
currValue = nextValue ;
nextFetched = numberWalker.MoveNext() ;
nextValue = nextFetched?numberWalker.Current:default( int ) ;
if ( currFetched && nextFetched ) position = ListPosition.Middle ;
else if ( currFetched && ! nextFetched ) position = ListPosition.Last ;
else if ( ! currFetched ) position = ListPosition.Exhausted ;
else throw new InvalidOperationException( "Reached Unreachable Code. Hmmm...that doesn't seem quite right" );
}
Console.WriteLine() ;
return ;
}
static void Main( string[] args )
{
List<int> list1 = new List<int>( new []{ 1 , } ) ;
List<int> list2 = new List<int>( new []{ 1 , 2 , } ) ;
List<int> list3 = new List<int>( new []{ 1 , 2 , 3 , } ) ;
List<int> list4 = new List<int>( new []{ 1 , 2 , 3 , 4 , } ) ;
Console.WriteLine( "List 1:" ) ; WalkList( list1 ) ;
Console.WriteLine( "List 2:" ) ; WalkList( list2 ) ;
Console.WriteLine( "List 3:" ) ; WalkList( list3 ) ;
Console.WriteLine( "List 4:" ) ; WalkList( list4 ) ;
return ;
}
}
}

Depends on your definition of "fancy". This might qualify:
if (myList.Count > 0)
{
myList.Take(myList.Count - 1)).ForEach(myString => doSomething(myString));
doSomethingElse(myList.Last());
}
To me, that seems close to what you're looking for. Note that it's not super high-peformance, but it's short and pretty readable, at least to my eyes. You could also write it this way:
if (myList.Count > 0)
{
foreach (string myString in myList.Take(myList.Count - 1))
{
doSomething(myString);
}
doSomethingElse(myList.Last());
}
Here's another alternative, which I'd consider the most obvious, but it's probably the fastest way of doing this:
if (myList.Count > 0)
{
for (int i = 0; i < myList.Count - 1; i++)
{
doSomething(myList[i]);
}
doSomethingElse(myList.Count - 1);
}

Related

How to make good use of BFS for sub milisecond execution?

I have an implementation of BFS that works just fine, but seems to get really CPU heavy, even at low depth (sub ms for a depth of 4 but 10 ms for a depth of 10). I'm quite confident that this algorithm should run sub ms event at a depth of 100 but i'm not quite sure what I'm missing.
Here's the code :
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class VisionGraph : MonoBehaviour
{
public Transform Ground;
public int Height;
public int Width;
private MeshFilter mf;
public Vector3[] Vertices;
public float precision;
public Vector3 SelectedVertex;
// Start is called before the first frame update
private void Start()
{
mf = Ground.GetComponent<MeshFilter>();
Matrix4x4 localToWorld = transform.localToWorldMatrix;
Vector3 world_max = localToWorld.MultiplyPoint3x4(mf.mesh.vertices[0]);
Width = Mathf.RoundToInt(world_max.z * (1 / precision));
int maxIndex = Mathf.RoundToInt((world_max.z * (1 / precision)) * (Height * (1 / precision)) * world_max.x);
Vertices = new Vector3[maxIndex];
//This is the graph initialization.
//Indices increment by 1 while actual coordinates increments by `precision`.
//Indices are then reversed to coordinates in BFS with `position/precision`.
int xInd, yInd, zInd; xInd = yInd = zInd = 0;
float x, y, z; x = y = z = 0;
int index = 0;
while (index < Vertices.Length - 1)
{
index = (yInd * (Width * Width)) + (zInd * Width) + xInd;
Debug.Log(index + " " + maxIndex);
Vertices[index] = new(x, y, z);
x += precision;
xInd++;
if (x > world_max.x)
{
x = 0;
xInd = 0;
z += precision;
zInd++;
if (z > world_max.z)
{
z = 0;
zInd = 0;
y += precision;
yInd++;
}
}
}
SelectedVertex = Vertices[600];
}
private void OnDrawGizmos()
{
// Needs to be turned into retrieve index from position.
// but i'm not sure how to clamp the continuous position to `precision` steps.
SelectedVertex = Vertices.Where(v => Vector3.Distance(v, SelectedVertex) <= precision - 0.0001 ).FirstOrDefault();
var watch = System.Diagnostics.Stopwatch.StartNew();
List<Vector3Int> closeVertices = BFS(SelectedVertex, 10); // second param is the search depth
watch.Stop();
Debug.Log(watch.ElapsedMilliseconds);
foreach (var vert in closeVertices)
{
var index = (vert.y * (Width * Width)) + (vert.z * Width) + vert.x;
if (index >= Vertices.Length) continue;
Gizmos.color = Color.red;
Gizmos.DrawSphere(Vertices[index], 0.1f);
}
}
private List<Vector3Int> BFS(Vector3 start, int depth)
{
Vector3Int startIndex = new((int)(start.x / precision), (int)(start.y / precision), (int)(start.z / precision));
Dictionary<Vector3Int, bool> closedList = new();
List<Vector3Int> queue = new() { startIndex };
while (queue.Count > 0)
{
Vector3Int v = queue[^1];
queue.RemoveAt(queue.Count-1);
Vector3Int[] neighbors = new[]
{
v + Vector3Int.left,
v + Vector3Int.right,
v + Vector3Int.up,
v + Vector3Int.down,
v + Vector3Int.forward,
v + Vector3Int.back,
};
foreach (Vector3Int n in neighbors)
{
if (n.x < 0 || n.y < 0 || n.z < 0) continue; // this will alos include the high limit of the grid but i dont "need" it at this point of the tests
//For every implementation of graph search algorithms I make, this always seem to bee the weak part.
if ((n - startIndex).sqrMagnitude > depth*depth || queue.Any(vert => vert == n) || closedList.ContainsKey(n)) continue;
queue.Insert(0, n);
}
closedList.Add(v, true);
}
return closedList.Keys.ToList();
}
}
The use of a Dictionary for the closedList is a poor attempt at trying to reduce the List searching time, it used to be closedList.Any(vert => vert == n) but I didn't see great change by the use of each.
I'd be really glad if somebody could pinpoint what really slow this down.
Second question : How do you even MultiThread with BFS ? Both the queue and the closed list are very dynamic, is there a solution to work this out with NativeLists ?
Thank you for your time. Let me know if anything's unclear.
Upfront, this may, or may not be suitable in your situation, but as an exercise for myself as much as anything, I thought it'd be interesting to see how fast I could get the execution of the given code.
I created three methods in my test.
The first was your method verbatim.
The second method was your code, but 'rejigged' but taking the
neighbours array creation out of the loop and adding in a
Queue<Vector3Int> instead of the List<Vector3Int> as I suspected
that the insertion to element 0 of the List<T> might be a big part of the issue in your original code also replacing your Dictionary<Vector3Int, bool> with a HashSet<Vector3Int>. I also replaced Any(v => v == n) with Contains(v). Contains turned out to be significantly faster.
Then the third method I created, I went even further to add a second HashSet to enable a fast lookup of items that were still in the queue.
I then ran the tests against each method using a StopWatch to record the times. I also ran the last method again, but that time from Task.Run.
Here are the two 'optimised' methods:
private List<Vector3Int> BFS_Optimised ( Vector3 start, int depth )
{
Vector3Int startIndex = new((int)(start.x / precision), (int)(start.y / precision), (int)(start.z / precision));
HashSet<Vector3Int> closedList = new ();
Queue<Vector3Int> queue = new ();
queue.Enqueue(startIndex);
var dSquared = depth * depth;
Vector3Int[] neighbors =
new[] { Vector3Int.left, Vector3Int.right, Vector3Int.up, Vector3Int.down, Vector3Int.forward, Vector3Int.back };
while ( queue.Count > 0 )
{
var v = queue.Dequeue();
for ( int i = 0; i < 6; ++i )
{
var n = v + neighbors[i];
if ( n.x < 0 || n.y < 0 || n.z < 0 )
continue; // this will alos include the high limit of the grid but i dont "need" it at this point of the tests
if ( ( n - startIndex ).sqrMagnitude > dSquared
|| closedList.Contains ( n )
|| queue.Contains ( n ) ) // queue.Any(v => v == n ) ) //
continue;
queue.Enqueue ( n );
}
closedList.Add ( v );
}
return closedList.ToList ( );
}
private List<Vector3Int> BFS_Optimised2 ( Vector3 start, int depth )
{
Vector3Int startIndex = new((int)(start.x / precision), (int)(start.y / precision), (int)(start.z / precision));
HashSet<Vector3Int> closedList = new ();
Queue<Vector3Int> queue = new ();
queue.Enqueue ( startIndex );
HashSet<Vector3Int> qHash = new ( ) { startIndex };
var dSquared = depth * depth;
Vector3Int[] neighbors =
new[] { Vector3Int.left, Vector3Int.right, Vector3Int.up, Vector3Int.down, Vector3Int.forward, Vector3Int.back };
while ( queue.Count > 0 )
{
var v = queue.Dequeue();
qHash.Remove ( v );
for ( int i = 0; i < 6; i++ )
{
var n = v + neighbors[i];
if ( n.x < 0 || n.y < 0 || n.z < 0 )
continue; // this will alos include the high limit of the grid but i dont "need" it at this point of the tests
if ( ( n - startIndex ).sqrMagnitude > dSquared
|| closedList.Contains ( n )
|| qHash.Contains ( n ) )
continue;
queue.Enqueue ( n );
qHash.Add ( n );
}
closedList.Add ( v );
}
return closedList.ToList ( );
}
Here's the test (excuse the crudeness and lack of depth in tests):
async void Run ( )
{
var iterations = 100;
var d = 10;
var v = new Vector3 ( 10, 10, 10 );
List<Vector3Int> r1 = default;
List<Vector3Int> r2 = default;
List<Vector3Int> r3 = default;
List<Vector3Int> r4 = default;
Debug.Log ( "Waiting ... " );
await Task.Delay ( 2000 );
Debug.Log ( "Run ... " );
Stopwatch sw = new();
sw.Start ( );
for ( int i = 0; i < iterations; i++ )
r1 = BFS ( v, d );
sw.Stop ( );
var t1 = sw.Elapsed.TotalMilliseconds;
sw.Restart ( );
for ( int i = 0; i < iterations; i++ )
r2 = BFS_Optimised ( v, d );
sw.Stop ( );
var t2 = sw.Elapsed.TotalMilliseconds;
sw.Restart ( );
for ( int i = 0; i < iterations; i++ )
r3 = BFS_Optimised2 ( v, d );
sw.Stop ( );
var t3 = sw.Elapsed.TotalMilliseconds;
sw.Restart ( );
r4 = await Task.Run ( ( ) => BFS_Optimised2 ( v, d ) );
sw.Stop ( );
var t4 = sw.Elapsed.TotalMilliseconds;
StringBuilder sb = new();
sb.AppendLine ( $"Original : {t1} ms [{r1.Count}] ({r1 [ 0 ]}) .. ({r1 [ ^1 ]})" );
sb.AppendLine ( $"Optimised : {t2} ms [{r2.Count}] ({r2 [ 0 ]}) .. ({r2 [ ^1 ]})" );
sb.AppendLine ( $"Optimised2 : {t3} ms [{r3.Count}] ({r3 [ 0 ]}) .. ({r3 [ ^1 ]})" );
sb.AppendLine ( $"Optimised2 Task.Run : {t4} ms [{r4.Count}] ({r4 [ 0 ]}) .. ({r4 [ ^1 ]})" );
Debug.Log ( sb.ToString ( ) );
}
And here are the results:
Original : 10701.7465 ms [4169] ((10, 10, 10)) .. ((15, 5, 3))
Optimised : 1830.9519 ms [4169] ((10, 10, 10)) .. ((15, 5, 3))
Optimised2 : 209.1559 ms [4169] ((10, 10, 10)) .. ((15, 5, 3))
Optimised2 Task.Run : 17.7353 ms [4169] ((10, 10, 10)) .. ((15, 5, 3))
I cannot make out at a glance what your code is doing ( there is no docs and my C# is rusty ) But your BFS has an surprising amount of code ( A BFS usually only takes a few lines ). Why do you need to create a new, large data structure on EVERY pass through the while loop? That is expensive, and as far as I recall C# uses some bedeviled garbage collector thingy that is going to be fighting you all the way
You have a variable named queue, but it is not a queue but rather a list. This is confusing, and probably incorrect.
As far as I can make out, you are creating the neighbors of a node on the fly as you go along. Terrible idea! You should model the graph either with an adjacency matrix ( simplest ) or node adjacency lists ( more complicated, but memory efficient for large graphs ) Then you can move through the graph using the node indices to look up the neighbours without needing to create them over and over again.
For comparison, here is a C++ code for a BFS that uses a real queue and concerns itself only with node indices.
void cPathFinder::breadth(
std::function<void(int v, int p)> visitor)
{
if (!nodeCount())
throw std::runtime_error("breadth called on empty graph");
std::vector<bool> visited(nodeCount(), false);
std::queue<int> Q;
visited[myStart] = true;
Q.push(myStart);
while (Q.size())
{
int v = Q.front();
Q.pop();
for (int w : adjacent(v))
{
if (!visited[w])
{
// reached a new node
visitor(w, v);
visited[w] = true;
Q.push(w);
}
}
}
}

String Combinations With Character Replacement

I am trying to work through a scenario I haven't seen before and am struggling to come up with an algorithm to implement this properly. Part of my problem is a hazy recollection of the proper terminology. I believe what I am needing is a variation of the standard "combination" problem, but I could well be off there.
The Scenario
Given an example string "100" (let's call it x), produce all combinations of x that swap out one of those 0 (zero) characters for a o (lower-case o). So, for the simple example of "100", I would expect this output:
"100"
"10o"
"1o0"
"1oo"
This would need to support varying length strings with varying numbers of 0 characters, but assume there would never be more than 5 instances of 0.
I have this very simple algorithm that works for my sample of "100" but falls apart for anything longer/more complicated:
public IEnumerable<string> Combinations(string input)
{
char[] buffer = new char[input.Length];
for(int i = 0; i != buffer.Length; ++i)
{
buffer[i] = input[i];
}
//return the original input
yield return new string(buffer);
//look for 0's and replace them
for(int i = 0; i != buffer.Length; ++i)
{
if (input[i] == '0')
{
buffer[i] = 'o';
yield return new string(buffer);
buffer[i] = '0';
}
}
//handle the replace-all scenario
yield return input.Replace("0", "o");
}
I have a nagging feeling that recursion could be my friend here, but I am struggling to figure out how to incorporate the conditional logic I need here.
Your guess was correct; recursion is your friend for this challenge. Here is a simple solution:
public static IEnumerable<string> Combinations(string input)
{
int firstZero = input.IndexOf('0'); // Get index of first '0'
if (firstZero == -1) // Base case: no further combinations
return new string[] { input };
string prefix = input.Substring(0, firstZero); // Substring preceding '0'
string suffix = input.Substring(firstZero + 1); // Substring succeeding '0'
// e.g. Suppose input was "fr0d00"
// Prefix is "fr"; suffix is "d00"
// Recursion: Generate all combinations of suffix
// e.g. "d00", "d0o", "do0", "doo"
var recursiveCombinations = Combinations(suffix);
// Return sequence in which each string is a concatenation of the
// prefix, either '0' or 'o', and one of the recursively-found suffixes
return
from chr in "0o" // char sequence equivalent to: new [] { '0', 'o' }
from recSuffix in recursiveCombinations
select prefix + chr + recSuffix;
}
This works for me:
public IEnumerable<string> Combinations(string input)
{
var head = input[0] == '0' //Do I have a `0`?
? new [] { "0", "o" } //If so output both `"0"` & `"o"`
: new [] { input[0].ToString() }; //Otherwise output the current character
var tails = input.Length > 1 //Is there any more string?
? Combinations(input.Substring(1)) //Yes, recursively compute
: new[] { "" }; //Otherwise, output empty string
//Now, join it up and return
return
from h in head
from t in tails
select h + t;
}
You don't need recursion here, you can enumerate your patterns and treat them as binary numbers. For example, if you have three zeros in your string, you get:
0 000 ....0..0....0...
1 001 ....0..0....o...
2 010 ....0..o....0...
3 011 ....0..o....o...
4 100 ....o..0....0...
5 101 ....o..0....o...
6 110 ....o..o....0...
7 111 ....o..o....o...
You can implement that with bitwise operators or by treating the chars that you want to replace like an odometer.
Below is an implementation in C. I'm not familiar with C# and from the other answers I see that C# already has suitable standard classes to implement what you want. (Although I'm surprised that so many peolpe propose recursion here.)
So this is more an explanation or illustration of my comment to the question than an implementation advice for your problem.
int binrep(char str[])
{
int zero[40]; // indices of zeros
int nzero = 0; // number of zeros in string
int ncombo = 1; // number of result strings
int i, j;
for (i = 0; str[i]; i++) {
if (str[i] == '0') {
zero[nzero++] = i;
ncombo <<= 1;
}
}
for (i = 0; i < ncombo; i++) {
for (j = 0; j < nzero; j++) {
str[zero[j]] = ((i >> j) & 1) ? 'o' : '0';
}
printf("%s\n", str); // should yield here
}
return ncombo;
}
Here's a solution using recursion, and your buffer array:
private static void Main(string[] args)
{
var a = Combinations("100");
var b = Combinations("10000");
}
public static IEnumerable<string> Combinations(string input)
{
var combinations = new List<string>();
combinations.Add(input);
for (int i = 0; i < input.Length; i++)
{
char[] buffer= input.ToArray();
if (buffer[i] == '0')
{
buffer[i] = 'o';
combinations.Add(new string(buffer));
combinations = combinations.Concat(Combinations(new string(buffer))).ToList();
}
}
return combinations.Distinct();
}
The method adds the raw input as the first result. After that, we replace in a loop the 0s we see as a o and call our method back with that new input, which will cover the case of multiple 0s.
Finally, we end up with a couple duplicates, so we use Distinct.
I know that the earlier answers are better. But I don't want my code to go to waste. :)
My approach for this combinatorics problem would be to take advantage of how binary numbers work. My algorithm would be as follows:
List<string> ZeroCombiner(string str)
{
// Get number of zeros.
var n = str.Count(c => c == '0');
var limit = (int)Math.Pow(2, n);
// Create strings of '0' and 'o' based on binary numbers from 0 to 2^n.
var binaryStrings = new List<string>();
for (int i = 0; i < limit; ++i )
{
binaryStrings.Add(Binary(i, n + 1));
}
// Replace each zero with respect to each binary string.
var result = new List<string>();
foreach (var binaryString in binaryStrings)
{
var zeroCounter = 0;
var combinedString = string.Empty;
for (int i = 0; i < str.Length; ++i )
{
if (str[i] == '0')
{
combinedString += binaryString[zeroCounter];
++zeroCounter;
}
else
combinedString += str[i];
}
result.Add(combinedString);
}
return result;
}
string Binary(int i, int n)
{
string result = string.Empty;
while (n != 0)
{
result = result + (i % 2 == 0 ? '0' : 'o');
i = i / 2;
--n;
}
return result;
}

Handling Permutations of If Statements in C#

I have some Lists which store values. Now I want to create If-Statements to handle this but that's to much. For example:
if(list1.Count==0 && list2.Count==0)
{
//do something
}
if(list1.Count==0 && list3.Count==0)
{
//do something
}
if(list1.Count==0 && list2.Count==0 && list3.Count==0)
{
//do something
}
so there is a huge amount of if-statements if I have about 10 Lists. Is there a better way to handle that? I haven't found anything useful.
Thanks!
One of the way that i can give the suggestion seeing the code pasted here is that you have some duplicated stuff like this
if(list1.Count==0 && list2.Count==0)
and then
if(list1.Count==0 && list2.Count==0 && list3.Count==0)
One of the suggestion will be to per-calculate the condition like this
bool onetwo = list1.Count==0 && list2.Count==0;
bool thirdalone = list3.Count == 0;
Now the code can be more better like this
if(onetwo){
}
if(onetwo && thirdalone){
}
If you wish you can use the Bitmasking to generate all of them for example , here n is total lists we have.
bool[] statu = new bool[1 << n];
for(int i = 1 ; i < (1<< n) ; i++){
bool result = true;
for(int j = 0 ; j < 32 ; j++){
if(i & ( 1 << j) > 0){
//this position is part of set
if(list[j].count == 0)
result = false;
}
}
status[i] = result;
}
But it is just more semantic way , nothing can be performance enhancement etc..
If you need to check every permutation you can do something like this:
bool b1 = ( list1.count == 0 );
bool b2 = ( list2.count == 0 );
bool b3 = ( list3.count == 0 );
bool b4 = ( list4.count == 0 );
// etc etc
BitArray arr = new BitArray(new bool[4] { b1, b2, b3, b4 });
byte[] bits = new byte[4];
arr.CopyTo(bits, 0);
int x = BitConverter.ToInt32(bits, 0);
switch (x)
{
case 1: // only list 1 is empty
case 2: // only list 2 is empty
case 3: // only list 1 and list 2 are empty
case x: // and so on.
}
I wouldn't say is it's any more readable, but I'd rather maintain something like this is future than a giant if/else/else if block.

Sort a List<StringBuilder>

Requirement: Iterate through a sorted list of strings, adding a char at the beginning of each string, then re-sorting. This may need to be done a few thousand times. I tried using a regular List of strings but, as expected, the process was way too slow.
I was going to try a List of StringBuilders but there is no direct way to sort the list. Any workarounds come to mind?
You've stated you can't sort a Link - however, you can if you can supply your own sort comparison:
List<StringBuilder> strings = new List<StringBuilder>();
// ...
strings.Sort((s1, s2) => s1.ToString().CompareTo(s2.ToString()));
The problem here as #phoog notes, is that in order to do so it allocates a lot of extra strings and isn't very efficient. The sort he provides is better. What we can do to figure out which approach is better is supply a test. You can see the fiddle here: http://dotnetfiddle.net/Px4fys
The fiddle uses very few strings and very few iterations because it's in a fiddle and there's a memory limit. If you paste this into a console app and run in Release you'll find there's huge differences. As #phoog also suggests LinkedList<char> wins hands-down. StringBuilder is the slowest.
If we bump up the values and run in Release mode:
const int NumStrings= 1000;
const int NumIterations= 1500;
We'll find the results:
List<StringBuilder> - Elapsed Milliseconds: 27,678
List<string> - Elapsed Milliseconds: 2,932
LinkedList<char> - Elapsed Milliseconds: 912
EDIT 2: When I bumped both values up to 3000 and 3000
List<StringBuilder> - Elapsed Milliseconds: // Had to comment out - was taking several minutes
List<string> - Elapsed Milliseconds: 45,928
LinkedList<char> - Elapsed Milliseconds: 6,823
The string builders will be a bit quicker than strings, but still slow, since you have to copy the entire buffer to add a character at the beginning.
You can create a custom comparison method (or comparer object if you prefer) and pass it to the List.Sort method:
int CompareStringBuilders(StringBuilder a, StringBuilder b)
{
for (int i = 0; i < a.Length && i < b.Length; i++)
{
var comparison = a[i].CompareTo(b[i]);
if (comparison != 0)
return comparison;
}
return a.Length.CompareTo(b.Length);
}
Invoke it like this:
var list = new List<StringBuilder>();
//...
list.Sort(CompareStringBuilders);
You would probably do better to look for a different solution to your problem, however.
Linked lists offer quick prepending, so how about using LinkedList<char>? This might not work if you need other StringBuilder functions, of course.
StringBuilder was rewritten for .NET 4, so I've struck out my earlier comments about slow prepending of characters. If performance is an issue, you should test to see where the problems actually lie.
Thanks to all for the suggestions posted. I checked these, and I have to say that I'm astonished that LinkedList works incredibly well, except for memory usage.
Another surprise is the slow sorting speed of the StringBuilder list. It works quickly as expected for the char insert phase. But the posted benchmarks above reflect what I've found: StringBuilder sorts very slowly for some reason. Painfully slow.
List of strings sorts faster. But counter to intuition, List of LinkedList sorts very fast. I have no idea how navigating a linked list could possibly be faster than simple indexing of a buffer (as in strings and StringBuilder), but it is. I would never have thought to try LinkedList. Compliments to McAden for the insight!
But unfortunately, LinkedList runs the system out of RAM. So, back to the drawing board.
Sort the StringBuilders as described in Phoog's answer, but keep the strings in reverse order in the StringBuilder instances - this way, you can optimize the "prepending" of each new character by appending it to the end of the StringBuilder's current value:
Update: with test program
class Program
{
static readonly Random _rng = new Random();
static void Main(string[] args)
{
int stringCount = 2500;
int initialStringSize = 100;
int maxRng = 4;
int numberOfPrepends = 2500;
int iterations = 5;
Console.WriteLine( "String Count: {0}; # of Prepends: {1}; # of Unique Chars: {2}", stringCount, numberOfPrepends, maxRng );
var startingStrings = new List<string>();
for( int i = 0; i < stringCount; ++i )
{
var sb = new StringBuilder( initialStringSize );
for( int j = 0; j < initialStringSize; ++j )
{
sb.Append( _rng.Next( 0, maxRng ) );
}
startingStrings.Add( sb.ToString() );
}
for( int i = 0; i < iterations; ++i )
{
TestUsingStringBuilderAppendWithReversedStrings( startingStrings, maxRng, numberOfPrepends );
TestUsingStringBuilderPrepend( startingStrings, maxRng, numberOfPrepends );
}
var input = Console.ReadLine();
}
private static void TestUsingStringBuilderAppendWithReversedStrings( IEnumerable<string> startingStrings, int maxRng, int numberOfPrepends )
{
var builders = new List<StringBuilder>();
var start = DateTime.Now;
foreach( var str in startingStrings )
{
builders.Add( new StringBuilder( str ).Reverse() );
}
for( int i = 0; i < numberOfPrepends; ++i )
{
foreach( var sb in builders )
{
sb.Append( _rng.Next( 0, maxRng ) );
}
builders.Sort( ( x, y ) =>
{
var comparison = 0;
var xOffset = x.Length;
var yOffset = y.Length;
while( 0 < xOffset && 0 < yOffset && 0 == comparison )
{
--xOffset;
--yOffset;
comparison = x[ xOffset ].CompareTo( y[ yOffset ] );
}
if( 0 != comparison )
{
return comparison;
}
return xOffset.CompareTo( yOffset );
} );
}
builders.ForEach( sb => sb.Reverse() );
var end = DateTime.Now;
Console.WriteLine( "StringBuilder Reverse Append - Total Milliseconds: {0}", end.Subtract( start ).TotalMilliseconds );
}
private static void TestUsingStringBuilderPrepend( IEnumerable<string> startingStrings, int maxRng, int numberOfPrepends )
{
var builders = new List<StringBuilder>();
var start = DateTime.Now;
foreach( var str in startingStrings )
{
builders.Add( new StringBuilder( str ) );
}
for( int i = 0; i < numberOfPrepends; ++i )
{
foreach( var sb in builders )
{
sb.Insert( 0, _rng.Next( 0, maxRng ) );
}
builders.Sort( ( x, y ) =>
{
var comparison = 0;
for( int offset = 0; offset < x.Length && offset < y.Length && 0 == comparison; ++offset )
{
comparison = x[ offset ].CompareTo( y[ offset ] );
}
if( 0 != comparison )
{
return comparison;
}
return x.Length.CompareTo( y.Length );
} );
}
var end = DateTime.Now;
Console.WriteLine( "StringBulder Prepend - Total Milliseconds: {0}", end.Subtract( start ).TotalMilliseconds );
}
}
public static class Extensions
{
public static StringBuilder Reverse( this StringBuilder stringBuilder )
{
var endOffset = stringBuilder.Length - 1;
char a;
for( int beginOffset = 0; beginOffset < endOffset; ++beginOffset, --endOffset )
{
a = stringBuilder[ beginOffset ];
stringBuilder[ beginOffset ] = stringBuilder[ endOffset ];
stringBuilder[ endOffset ] = a;
}
return stringBuilder;
}
}
results:
2500 strings initially at 100 characters, 2500 prepends:

Merging two lists into one and sorting the items

Is there a way to merge(union without dupes) two given lists into one and store the items in sorted way by using ONE for loop?
Also, i am looking for a solution which does not makes use of API methods ( like, union, sort etc).
Sample Code.
private static void MergeAndOrder()
{
var listOne = new List<int> {3, 4, 1, 2, 7, 6, 9, 11};
var listTwo = new List<int> {1, 7, 8, 3, 5, 10, 15, 12};
//Without Using C# helper methods...
//ToDo.............................
//Using C# APi.
var expectedResult = listOne.Union(listTwo).ToList();
expectedResult.Sort();//Output: 1,2,3,4,5,6,7,8,9,10,11,12,15
//I need the same result without using API methods, and that too by iterating over items only once.
}
PS: I have been asked this question in an interview, but couldn't find answer as yet.
Why can't you use the api methods? Re-inventing the wheel is dumb. Also, it's the .ToList() call that's killing you. Never call .ToList() or .ToArray() until you absolutely have to, because they break your lazy evaluation.
Do it like this and you'll enumerate the lists with the minimum amount necessary:
var expectedResult = listOne.Union(listTwo).OrderBy(i => i);
This will do the union in one loop using a hashset, and lazy execution means the base-pass for the sort will piggyback on the union. But I don't think it's possible finish the sort in a single iteration, because sorting is not a O(n) operation.
Without the precondition that both lists are sorted before the merge + sort operation, you can't do this in O(n) time (or "using one loop").
Add that precondition and the problem is very easy.
Keep two iterators, one for each list. On each loop, compare the element from each list and choose the smaller. Increment that list's iterator. If the element you are about to insert in the final list is already the last element in that list, skip the insert.
In pseudocode:
List a = { 1, 3, 5, 7, 9 }
List b = { 2, 4, 6, 8, 10 }
List result = { }
int i=0, j=0, lastIndex=0
while(i < a.length || j < b.length)
// If we're done with a, just gobble up b (but don't add duplicates)
if(i >= a.length)
if(result[lastIndex] != b[j])
result[++lastIndex] = b[j]
j++
continue
// If we're done with b, just gobble up a (but don't add duplicates)
if(j >= b.length)
if(result[lastIndex] != a[i])
result[++lastIndex] = a[i]
i++
continue
int smallestVal
// Choose the smaller of a or b
if(a[i] < b[j])
smallestVal = a[i++]
else
smallestVal = b[j++]
// Don't insert duplicates
if(result[lastIndex] != smallestVal)
result[++lastIndex] = smallestVal
end while
private static void MergeTwoSortedArray(int[] first, int[] second)
{
//throw new NotImplementedException();
int[] result = new int[first.Length + second.Length];
int i=0 , j=0 , k=0;
while(i < first.Length && j <second.Length)
{
if(first[i] < second[j])
{
result[k++] = first[i++];
}
else
{
result[k++] = second[j++];
}
}
if (i < first.Length)
{
for (int a = i; a < first.Length; a++)
result[k] = first[a];
}
if (j < second.Length)
{
for (int a = j; a < second.Length; a++)
result[k++] = second[a];
}
foreach (int a in result)
Console.Write(a + " ");
Console.WriteLine();
}
Using iterators and streaming interface the task is not that complicated:
class MergeTwoSortedLists
{
static void Main(string[] args) {
var list1 = new List<int?>() {
1,3,5,9,11
};
var list2 = new List<int?>() {
2,5,6,11,15,17,19,29
};
foreach (var c in SortedAndMerged(list1.GetEnumerator(), list2.GetEnumerator())) {
Console.Write(c+" ");
}
Console.ReadKey();
}
private static IEnumerable<int> SortedAndMerged(IEnumerator<int?> e1, IEnumerator<int?> e2) {
e2.MoveNext();
e1.MoveNext();
do {
while (e1.Current < e2.Current) {
if (e1.Current != null) yield return e1.Current.Value;
e1.MoveNext();
}
if (e2.Current != null) yield return e2.Current.Value;
e2.MoveNext();
} while (!(e1.Current == null && e2.Current == null));
}
}
Try this:
public static IEnumerable<T> MergeWith<T>(IEnumerable<T> collection1, IEnumerable<T> collection2,
IComparer<T> comparer)
{
using (var enumerator1 = collection1.GetEnumerator())
using (var enumerator2 = collection2.GetEnumerator())
{
var isMoveNext1 = enumerator1.MoveNext();
var isMoveNext2 = enumerator2.MoveNext();
do
{
while (comparer.Compare(enumerator1.Current, enumerator2.Current) < 0 || !isMoveNext2)
{
if (isMoveNext1)
yield return enumerator1.Current;
else
break;
isMoveNext1 = enumerator1.MoveNext();
}
if (isMoveNext2)
yield return enumerator2.Current;
isMoveNext2 = enumerator2.MoveNext();
} while (isMoveNext1 || isMoveNext2);
}
}
You could write a loop that merges and de-dups the lists and uses a binary-search approach to insert new values into the destination list.
var listOne = new List<int> { 3, 4, 1, 2, 7, 6, 9, 11 };
var listTwo = new List<int> { 1, 7, 8, 3, 5, 10, 15, 12 };
var result = listOne.ToList();
foreach (var n in listTwo)
{
if (result.IndexOf(n) == -1)
result.Add(n);
}
The closest solution I see would be to allocate an array knowing that integers are bounded to some value.
int[] values = new int[ Integer.MAX ]; // initialize with 0
int size1 = list1.size();
int size2 = list2.size();
for( int pos = 0; pos < size1 + size2 ; pos++ )
{
int val = pos > size1 ? list2[ pos-size1 ] : list1[ pos ] ;
values[ val ]++;
}
Then you can argue that you have the sorted array in a "special" form :-) To get a clean sorted array, you need to traverse the values array, skip all position with 0 count, and build the final list.
This will only work for lists of integers, but happily that is what you have!
List<int> sortedList = new List<int>();
foreach (int x in listOne)
{
sortedList<x> = x;
}
foreach (int x in listTwo)
{
sortedList<x> = x;
}
This is using the values in each list as the index position at which to store the value. Any duplicate values will overwrite the previous entry at that index position. It meets the requirement of only one iteration over the values.
It does of course mean that there will be 'empty' positions in the list.
I suspect the job position has been filled by now though.... :-)

Categories