How to rewrite LINQ-based code as a for loop - c#

I'm using the following code:
public class Card
{
public Card(Rank rank, Suit suit)
{
this.Rank = rank;
this.Suit = suit;
}
public Rank Rank { get; private set; }
public Suit Suit { get; private set; }
}
public enum Rank : byte
{
Ace = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9,
Ten = 10,
Jack = 11,
Queen = 12,
King = 13,
}
public enum Suit : byte
{
Club = 1, // Klavers
Diamond = 2, // Ruiten
Heart = 3, // Harten
Spades = 4 // Schoppen
}
public class Deck
{
private List<Card> deck = new List<Card>(52);
public Deck()
{
this.FillStack();
}
public void FillStack()
{
this.deck.Clear();
this.deck.AddRange(
Enumerable.Range(1,4)
.SelectMany( s =>
Enumerable.Range(1, 13)
.Select( n => new Card( (Rank)n , (Suit)s ) )
)
) ;
}
Can I replace the LINQ-based code in FillStack with equivalent code based on a for loop? The goal is to make the code easier to understand and extend for those who are less familiar with LINQ.

Yes, this looks like a good example of how not to use the LINQ helper methods. There is no advantage over a plain old and far more readable
for (int s = 1; s <= 4; s++)
for (int n = 1; n <= 13; n++)
this.deck.Add(new Card((Rank)n, (Suit)s));
The LINQ version could be made somewhat more readable though: here's how it could look when not attempting to stuff it all into a single expression.
var suits = Enumerable.Range(1, 4);
var ranks = Enumerable.Range(1, 13);
var cards =
from s in suits
from n in ranks
select new Card((Rank)n, (Suit)s);
this.deck.AddRange(cards);
This should be easier to understand, but it still means pretty much the same thing.

You could just say something like this
public void FillStack()
{
this.deck.Clear() ;
this.deck.AddRange(
Enumerable
.Range(0,52)
.Select( n => new Card( (Rank)(1+n%13), (Suit)(1+n/13) ) )
) ;
return ;
}
Or even
public void FillStack()
{
this.deck.Clear() ;
foreach ( Rank rank in Enum.GetValues(typeof(Rank)) )
{
foreach( Suit suit in Enum.GetValues(typeof(Suit)) )
{
Card card = new Card( rank , suit ) ;
this.deck.Add( card ) ;
}
}
return;
}

Your question is to replace the LINQ query with for loop so it's easier to understand. That's fair enough. But since you are learning it's worth to understand the query itself. Then you will be able to replace it yourself or, better yet, you will be comfortable enough with it to leave it there.
Let's break it down and try to explain it line by line:
[1] this.deck
[2] .AddRange(
[3] Enumerable.Range(1, 4)
[4] .SelectMany(s =>
[5] Enumerable.Range(1, 13)
[6] .Select(n =>
[7] new Card((Rank)n, (Suit)s))));
Start from the line #3 Enumerable.Range(1,4) - the method returns range of numbers from 1 to 4. So we have a collection 1, 2, 3, 4. In line #4 we access this collection item by item. So we actually iterate over the items. Each item is denoted by a variable s - you can use it to access the items. The same mechanism is repeated to create iteration from 1 to 13 (lines #5 and #6). The items in the second iteration are accessed by variable n.
Bigger picture - we iterate from 1 to 4 and inside that we iterate from 1 to 13. Now, in line #7 we create a Card and the numbers from both iterations - denoted as n and s - are used in the constructor of the class. Because we iterate through number from 1 to 4 and inside that from 1 to 13 we will get all the combinations: (1,1) (1,2) (1,3) (1,4) (2,1) (2,2) and so on. Each item from the combination will create a card. So it will result in a collection of cards. And now go back to line #2 AddRange method is responsible for adding this range of cards to the deck collection.
Hope this will help to understand the query and replace it with more imperative code.

Related

Ranking sums from first to third c#

I want to rank the number i added up from first to third but i cant think of a way to rank it properly since when there is a duplicate it will only show the number once and continues to the second highest
im new to the language and it would be great for someone to help me on this
Edit: Sorry i think there is a misunderstanding here my sums are in an array that is connected to the names in another array and im trying to sort it out with the same index value
Edit 2: Also i am stuck at c# 7.3 so i cant use some of the new codes
int first = Int32.MinValue;
int fs, nd, thr;
int temp = 0;
for (fs = 0; fs < hounds; fs++)
{
if (score_SUM[fs] > first)
{
first = score_SUM[fs];
temp = fs;
}
}
Console.WriteLine("\n" + "First:{1} {0}", first, houndname[temp]);
int second = Int32.MinValue;
for (nd = 0; nd < hounds; nd++)
{
if (score_SUM[nd] > second && score_SUM[nd] < first)
{
second = score_SUM[nd];
temp = nd;
}
}
Console.WriteLine("Second:{1} {0}", second, houndname[temp]);
int third = Int32.MinValue;
for (thr = 0; thr < hounds; thr++)
{
if (score_SUM[thr] > third && score_SUM[thr] < second)
{
third = score_SUM[thr];
temp = thr;
}
}
Console.WriteLine("Third:{1} {0}", third, houndname[temp]);
Console.ReadLine();
example
10 , 5 , 10 , 6, 1
The output will be like
10
6
5
But I expected
10
10
6
but i cant find a way to write a block a code for that
You're drastically over-engineering this.
If what you have is an array (or list/collection of some kind) of values then you can simply sort that array (descending in this case) and display the first three values.
For example, consider this list of values:
var hounds = new List<int> { 10, 5, 10, 6, 1 };
Then you can sort that list:
hounds = hounds.OrderByDescending(h => h).ToList();
And, either in a loop or by directly referencing the first three (if you know there will always be at least three), output them. For example:
Console.WriteLine("First:{0}", hounds[0]);
Console.WriteLine("Second:{0}", hounds[1]);
Console.WriteLine("Third:{0}", hounds[2]);
Regarding your edit...
my sums are in an array that is connected to the names in another array and im trying to sort it out with the same index value
You're doing it wrong.
Instead of trying to manually keep multiple arrays of values synchronized, maintain one array of meaningful objects. For example, consider how you define a "hound":
public class Hound
{
public int Value { get; set; }
public string Name { get; set; }
}
Create your list of hounds, not multiple lists of disconnected and unrelated values that you need to manually remember and keep synchronized. For example:
var hounds = new List<Hound>
{
new Hound { Value = 10, Name = "Fido" },
new Hound { Value = 5, Name = "Barney" },
new Hound { Value = 10, Name = "Jake" },
new Hound { Value = 6, Name = "Woof" },
new Hound { Value = 1, Name = "Dog" }
};
The rest of the process is the same. Sort the list:
hounds = hounds.OrderByDescending(h => h.Value);
And output the data:
Console.WriteLine("First:{1} {0}", hounds[0].Value, hounds[0].Name);
Console.WriteLine("Second:{1} {0}", hounds[1].Value, hounds[1].Name);
Console.WriteLine("Third:{1} {0}", hounds[2].Value, hounds[2].Name);
Overall, the main point here is that you don't need a ton of convoluted logic just to get the top 3 values in a list. Sorting is a common and well-established operation. All you need is the right data structure to be sorted.
Or, as usual, someone else has already said it better before...
"Smart data structures and dumb code works a lot better than the other way around."
Eric S. Raymond, The Cathedral & the Bazaar
Does this answer your question?
List<int> list = new() { 10, 5, 10, 6, 1 };
list.Sort((x, y) => y.CompareTo(x));
for (int i = 0; i < 3; i++)
{
Console.WriteLine(list[i]);
}
If you only want the three highest values you can also do this:
List<int> list = new() { 10, 5, 10, 6, 1 };
IEnumerable<int> highestValues = list.OrderByDescending(x => x).Take(3);
foreach (int value in highestValues)
{
Console.WriteLine(value);
}
Just change your < symbols to <= symbols. So your checks for the second and third ranks would look something like this:
// check for second
if (score_SUM[nd] > second && score_SUM[nd] <= first)
...
// check for third
if (score_SUM[thr] > third && score_SUM[thr] <= second)
...

set a sequence on a list of items

I need to set up a recursive function in C# to set the sequence number of a list of items. More specifically a bom. For each bom level, I need to start the sequence at 10, and increment of 10. How do I keep track of what level i'm at, and what counter to increment. This is driving me nuts.
Short example of data below, the real boms have thousands of lines and up to 12-15 levels.
Order
Level
Sequence
1
1
10
2
2
10
3
3
10
4
3
20
5
2
20
6
3
10
7
4
10
8
3
20
9
4
10
10
4
20
11
2
30
12
3
10
13
1
20
14
1
30
I indented the levels, to make the structure a bit more clear. And pasted the results of your answer to this. As you can see, the new levels are not sequenced properly.
I think i this case we can use the new language feature local function, and see if a recursive function is really necessary, as they are generally some of the hardest code to debug and maintain, only to be used sparingly, if at all this year, for any given year :)
[Fact]
public void SequencingRecursiveTest()
{
// BOM like byte order mark in utf 8 text encoding? odd problem :D
// Anyway we have a long list of values and want to emit the value with a sequence number, starting from 10 with increment 10
// Like in most cases when contemplating recursion, first off what about not using recursion to keep code maintainable and clean,
// As it turns out, we can:
//However super sneakily we have to reset all 'bom' sequence counts below the highest when an element in the sequence breaks the chain of same or greater
var keyValues = new Dictionary<int, int>();
var firstValue = 10;
var increment = 10;
int lastBom = 0;
int greatesBom = 0;
KeyValuePair<int, int> GetValueWithSequenceResetIfLowerThanLast(int bom)
{
bool reset = bom < lastBom;
greatesBom = bom > greatesBom ? bom : greatesBom;
if (reset)
{
foreach (int keyBom in keyValues.Keys)
{
if (keyBom < greatesBom)
keyValues[keyBom] = firstValue;
}
}
else if (keyValues.ContainsKey(bom))
{
keyValues[bom] = keyValues[bom] + increment;
}
else
{
keyValues.Add(bom, firstValue);
}
lastBom = bom;
return new KeyValuePair<int, int>(bom, keyValues[bom]);
}
var valueList = new List<int> { 1, 2, 3, 3, 2, 3, 4, 3, 4, 4, 2, 3, 1, 1 };
var valueSequenceList = valueList.Aggregate(
new List<KeyValuePair<int, int>>(),
(source, item) =>
{
source.Add(GetValueWithSequenceResetIfLowerThanLast(item));
return source;
}
);
foreach (var element in valueSequenceList)
System.Diagnostics.Debug.WriteLine($"{element.Key}: {element.Value}");
}

Check times a value is next to the same value in an array

Lets say I have this array on C#:
int myList = {1,4,6,8,3,3,3,3,8,9,0}
I want to know if a value (lets say from 0-9) is next to itself in the list and how many times. In this case, the value 3 is next to itself and it has 4 repetitions. If I have a list {0,1,2,3,4,5,5,6,7} the value 5 is next to itself and has 2 repetitions.
Repetitions have a limit of 5. No value can be repeated more than 5 times. The far I went is making if statements, but I know there's a better way of doing it.
The standard of question is not that good but writing the answer
int lastValue = myList[0];
int times = 0;
foreach (int value in myList) {
if (lastValue == value) {
times++;
}
else if (times <= 1) {
lastValue = value;
times = 1;
}
else
break;
}
You only have to iterate on your list and keep a counter that will count only the consecutive duplicate integer.
If you want a neater solution, you might look at using an open source library called morelinq (by Jon Skeet and few others) on nuget. It has useful extension methods for LINQ.
One of them is called GroupAdjacent, which is applicable to your problem.
var testList = new[] { 1, 4, 6, 8, 3, 3, 3, 3, 8, 9, 0 };
var groups = testList.GroupAdjacent(t => t);
var groupsWithMoreThanOneMember = groups.Where(g => g.Count() > 1);

How to merge 2 sorted listed into one shuffled list while keeping internal order in c#

I want to generate a shuffled merged list that will keep the internal order of the lists.
For example:
list A: 11 22 33
list B: 6 7 8
valid result: 11 22 6 33 7 8
invalid result: 22 11 7 6 33 8
Just randomly select a list (e.g. generate a random number between 0 and 1, if < 0.5 list A, otherwise list B) and then take the element from that list and add it to you new list. Repeat until you have no elements left in each list.
Generate A.Length random integers in the interval [0, B.Length). Sort the random numbers, then iterate i from 0..A.Length adding A[i] to into position r[i]+i in B. The +i is because you're shifting the original values in B to the right as you insert values from A.
This will be as random as your RNG.
None of the answers provided in this page work if you need the outputs to be uniformly distributed.
To illustrate my examples, assume we are merging two lists A=[1,2,3], B=[a,b,c]
In the approach mentioned in most answers (i.e. merging two lists a la mergesort, but choosing a list head randomly each time), the output [1 a 2 b 3 c] is far less likely than [1 2 3 a b c]. Intuitively, this happens because when you run out of elements in a list, then the elements on the other list are appended at the end. Because of that, the probability for the first case is 0.5*0.5*0.5 = 0.5^3 = 0.125, but in the second case, there are more random random events, since a random head has to be picked 5 times instead of just 3, leaving us with a probability of 0.5^5 = 0.03125. An empirical evaluation also easily validates these results.
The answer suggested by #marcog is almost correct. However, there is an issue where the distribution of r is not uniform after sorting it. This happens because original lists [0,1,2], [2,1,0], [2,1,0] all get sorted into [0,1,2], making this sorted r more likely than, for example, [0,0,0] for which there is only one possibility.
There is a clever way of generating the list r in such a way that it is uniformly distributed, as seen in this Math StackExchange question: https://math.stackexchange.com/questions/3218854/randomly-generate-a-sorted-set-with-uniform-distribution
To summarize the answer to that question, you must sample |B| elements (uniformly at random, and without repetition) from the set {0,1,..|A|+|B|-1}, sort the result and then subtract its index to each element in this new list. The result is the list r that can be used in replacement at #marcog's answer.
Original Answer:
static IEnumerable<T> MergeShuffle<T>(IEnumerable<T> lista, IEnumerable<T> listb)
{
var first = lista.GetEnumerator();
var second = listb.GetEnumerator();
var rand = new Random();
bool exhaustedA = false;
bool exhaustedB = false;
while (!(exhaustedA && exhaustedB))
{
bool found = false;
if (!exhaustedB && (exhaustedA || rand.Next(0, 2) == 0))
{
exhaustedB = !(found = second.MoveNext());
if (found)
yield return second.Current;
}
if (!found && !exhaustedA)
{
exhaustedA = !(found = first.MoveNext());
if (found)
yield return first.Current;
}
}
}
Second answer based on marcog's answer
static IEnumerable<T> MergeShuffle<T>(IEnumerable<T> lista, IEnumerable<T> listb)
{
int total = lista.Count() + listb.Count();
var random = new Random();
var indexes = Enumerable.Range(0, total-1)
.OrderBy(_=>random.NextDouble())
.Take(lista.Count())
.OrderBy(x=>x)
.ToList();
var first = lista.GetEnumerator();
var second = listb.GetEnumerator();
for (int i = 0; i < total; i++)
if (indexes.Contains(i))
{
first.MoveNext();
yield return first.Current;
}
else
{
second.MoveNext();
yield return second.Current;
}
}
Rather than generating a list of indices, this can be done by adjusting the probabilities based on the number of elements left in each list. On each iteration, A will have A_size elements remaining, and B will have B_size elements remaining. Choose a random number R from 1..(A_size + B_size). If R <= A_size, then use an element from A as the next element in the output. Otherwise use an element from B.
int A[] = {11, 22, 33}, A_pos = 0, A_remaining = 3;
int B[] = {6, 7, 8}, B_pos = 0, B_remaining = 3;
while (A_remaining || B_remaining) {
int r = rand() % (A_remaining + B_remaining);
if (r < A_remaining) {
printf("%d ", A[A_pos++]);
A_remaining--;
} else {
printf("%d ", B[B_pos++]);
B_remaining--;
}
}
printf("\n");
As a list gets smaller, the probability an element gets chosen from it will decrease.
This can be scaled to multiple lists. For example, given lists A, B, and C with sizes A_size, B_size, and C_size, choose R in 1..(A_size+B_size+C_size). If R <= A_size, use an element from A. Otherwise, if R <= A_size+B_size use an element from B. Otherwise C.
Here is a solution that ensures a uniformly distributed output, and is easy to reason why. The idea is first to generate a list of tokens, where each token represent an element of a specific list, but not a specific element. For example for two lists having 3 elements each, we generate this list of tokens: 0, 0, 0, 1, 1, 1. Then we shuffle the tokens. Finally we yield an element for each token, selecting the next element from the corresponding original list.
public static IEnumerable<T> MergeShufflePreservingOrder<T>(
params IEnumerable<T>[] sources)
{
var random = new Random();
var queues = sources
.Select(source => new Queue<T>(source))
.ToArray();
var tokens = queues
.SelectMany((queue, i) => Enumerable.Repeat(i, queue.Count))
.ToArray();
Shuffle(tokens);
return tokens.Select(token => queues[token].Dequeue());
void Shuffle(int[] array)
{
for (int i = 0; i < array.Length; i++)
{
int j = random.Next(i, array.Length);
if (i == j) continue;
if (array[i] == array[j]) continue;
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
Usage example:
var list1 = "ABCDEFGHIJKL".ToCharArray();
var list2 = "abcd".ToCharArray();
var list3 = "#".ToCharArray();
var merged = MergeShufflePreservingOrder(list1, list2, list3);
Console.WriteLine(String.Join("", merged));
Output:
ABCDaEFGHIb#cJKLd
This might be easier, assuming you have a list of three values in order that match 3 values in another table.
You can also sequence with the identity using identity (1,2)
Create TABLE #tmp1 (ID int identity(1,1),firstvalue char(2),secondvalue char(2))
Create TABLE #tmp2 (ID int identity(1,1),firstvalue char(2),secondvalue char(2))
Insert into #tmp1(firstvalue,secondvalue) Select firstvalue,null secondvalue from firsttable
Insert into #tmp2(firstvalue,secondvalue) Select null firstvalue,secondvalue from secondtable
Select a.firstvalue,b.secondvalue from #tmp1 a join #tmp2 b on a.id=b.id
DROP TABLE #tmp1
DROP TABLE #tmp2

C# Calculate items in List<int> values vertically

I have a list of int values some thing like below (upper bound and lower bounds are dynamic)
1, 2, 3
4, 6, 0
5, 7, 1
I want to calculate the column values in vertical wise like
1 + 4 + 5 = 10
2 + 6 + 7 = 15
3 + 0 + 1 = 4
Expected Result = 10,15,4
Any help would be appreciated
Thanks
Deepu
Here's the input data using array literals, but the subsequent code works exactly the same on arrays or lists.
var grid = new []
{
new [] {1, 2, 3},
new [] {4, 6, 0},
new [] {5, 7, 1},
};
Now produce a sequence with one item for each column (take the number of elements in the shortest row), in which the value of the item is the sum of the row[column] value:
var totals = Enumerable.Range(0, grid.Min(row => row.Count()))
.Select(column => grid.Sum(row => row[column]));
Print that:
foreach (var total in totals)
Console.WriteLine(total);
If you use a 2D array you can just sum the first, second,... column of each row.
If you use a 1D array you can simply use a modulo:
int[] results = new results[colCount];
for(int i=0, i<list.Count; list++)
{
results[i%colCount] += list[i];
}
Do you have to use a "List"-object? Elseway, I would use a twodimensional array.
Otherwise, you simply could try, how to reach rows and columns separatly, so you can add the numbers within a simply for-loop. It depends on the methods of the List-object.
Quite inflexible based on the question, but how about:
int ans = 0;
for(int i = 0; i < list.length; i+=3)
{
ans+= list[i];
}
You could either run the same thing 3 times with a different initial iterator value, or put the whole thing in another loop with startValue as an interator that runs 3 times.
Having said this, you may want to a) look at a different way of storing your data if, indeed they are in a single list b) look at more flexible ways to to this or wrap in to a function which allows you to take in to account different column numbers etc...
Cheers,
Adam

Categories