set a sequence on a list of items - c#

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}");
}

Related

Average every 20 numbers in an array

I have an a large array ( +400 numbers) decimal[] Raw where I need to average every 20 numbers, send those numbers to a new array, or list RawAvgList,
then form a new array, or list to get that average of the numbers in RawAvgList. Aka my code should find the average of the first 20 numbers and stores them in my new array/list, then next 20, then next 20. It should also for count if there are more or less then 20 number at the end of the large array
Should my while loop be in another loop that restarts the counting index??
Should I just be removing every 20 numbers as I go? I know just simply using the Average() on the decimal[] Raw is an option but the numbers needs to be more exact then that function can give. I have also tried using IndexRange but when the number isn't divisible by my count (20) it give and error, which will happen.
I have just been stumped for so long I am at my wits end and frustrated beyond belief, anything to help.
int unitof = 20;
decimal[] Raw = new decimal[] { Decimal.Parse(line.Substring(9).ToString(), style1) };
for (int i = 0; i < Raw.Length; i++)
{
while (count < Raw.Count())
{
RawAvgList.Add(// ** Average of every 20 numbers **//);
count += unitof; // 20 by 20 counter
}
// Reset counter or add another counter??
}
Edit (8/22/2022)
I added the IEnumerable<IEnumerable> Chunk as suggested, but I believe something else went wrong or I didn't fully understand how it worked because i have never used chunks.
I implemented the Chunk
public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> values, int chunkSize)
{
return values
.Select((v, i) => new { v, groupIndex = i / chunkSize })
.GroupBy(x => x.groupIndex)
.Select(g => g.Select(x => x.v));
}
added what you suggested
var rawAvgList = Raw.Chunk(20).Select(chunk => chunk.Average()).ToArray();
var result = rawAvgList.Average();
and then tried printing to the Console.Writeline()
Console.WriteLine($"{result} \t " + LengthRaw++);
Which got me and output of
36.41 0
37.94 1
38.35 2
37.63 3
36.41 4
36.41 5
36.21 6
36.82 7
37.43 8
37.43 9
37.43 10
37.43 11
37.43 12
37.94 13
37.94 14
37.84 15
37.43 16
37.84 17
37.43 18
37.84 19
37.84 20
When the output should be ( I am only using 21 numbers at the moment but it will be more then that later)
37.37 0
37.84 1
You can use Enumerable.Chunk() to split the data into batches of at most 20, and then average all those chunks:
decimal[] raw = new decimal[10000]; // Fill with your data.,
var rawAvgList = raw.Chunk(20).Select(chunk => chunk.Average()).ToArray();
var result = rawAvgList.Average();
However I don't know what you meant by
It should also for count if there are more or less then 20 number at
the end of the large array
The last block which will be averaged will be less than 20 long if the input is not a multiple of 20 items long, but all other blocks will be exactly 20 long.

C# Code to print out prime numbers from 5 to N

Prime Number Generator Code
Do know that this question should be quite basic but i have spent hours trying to figure out why my code is stuck in the loop as below. Have added a Console.WriteLine($"{counttemp} , {count1} "); in the if loop to check the 2 numbers and seems like it is not breaking out of the if condition when the condition is true
this is the console output for the writeline in the if loop
5 , 5
6 , 2
7 , 7
8 , 2
9 , 3
10 , 2
11 , 11
12 , 2
13 , 13
14 , 2
15 , 3
16 , 2
17 , 17
18 , 2
19 , 19
Problematic Loop??
for (count1 = 2; count1 <= counttemp ; ++count1)
{
if(counttemp % count1 == 0)
{
Console.WriteLine($"{counttemp} , {count1} ");
Console.ReadKey();
primetest1 = 0;
break;
}
}
full code sequence
static void Main(string[] args)
{
int prime1 = 10000, count1, primetest1, counttemp;
for (counttemp = 5; counttemp <= prime1; counttemp++)
{
primetest1 = 1;
for (count1 = 2; count1 <= counttemp ; ++count1)
{
if(counttemp % count1 == 0)
{
Console.WriteLine($"{counttemp} , {count1} ");
Console.ReadKey();
primetest1 = 0;
break;
}
}
if (primetest1 == 1)
{
Console.Write($"{counttemp}");
}
}
}
You're almost there. The problem is that you're checking if your candidate number is a prime by getting the remainder when divided by each number up to and including the number itself.
I think you'll find that N is a factor of N for all values of N. To fix this, you should only be checking up to but excluding the number.
And, as an aside, you don't really need to check all the way up to N - 1. You only need to go to the square root of N, adjusted up to the nearest integer. That's because, if it has a factor above the square root, you would already have found a factor below it.
Consider 24 as an example. It has 6, 8, and 12 as factors above the square root, but the matching values below the square root are 4, 3, and 2 respectively.
And there's a another trick you can use by realising that if a number is a multiple of a non-prime, it's also a multiple of every prime factor of that non-prime. In other words, every multiple of 12 is also a multiple of 2 and 3.
So you only need to check prime numbers up to the square root, to see if there's a factor. And prime numbers, other than two or three, are guaranteed to be of the form 6x-1 or 6x+1, so it's quite easy to filter out a large chunk of candidates very quickly, by checking only for those values.
In other words, check two and three as special cases. Then start at 5 and alternately add 2 and 4: 5, 7, 11, 13, 17, 19, .... Not every number in that set is prime (e.g, 25) every prime is guaranteed to be in that set.
You can check out an earlier answer of mine for more detail on why this is so, and how to do this sequence efficiently.

C# - Selecting data from an array when the data is random in content and of unknown length

So I wrote some code and was told it was poor form to write it in this way.
So hoping someone can point me towards a better way. My code:
string phrase = Value.Text;
List<string> wordList = new List<string>();
string words = phrase.Split('\t', '\r');
int Three = 3;
int Two = 2;
int One = 1;
int Four = 4;
int Five = 5;
int Six = 6;
for (int i = 0; i < result; i = i + 12, Three = Three + 12,
Two = Two + 12, One = One + 12, Four = Four + 12, Five = Five + 12,
Six = Six + 12)
{
wordList.Add(ID.Text + '\t');
wordList.Add(IT.Text + '\t');
wordList.Add(words[Three] + '\t');
wordList.Add(words[Two] + '\t');
wordList.Add(PD.Text + '\t');
....etc.
}
The offending part is this:
for (int i = 0; i < result; i = i + 12, Three = Three + 12,
Two = Two + 12, One = One + 12, Four = Four + 12, Five = Five + 12,
Six = Six + 12)
I can see why, it is pretty ugly and quite long, perhaps unnecessarily so?
What are you trying to do?
I have an array of data (phrase) that is entirely random.
The data is entered in sets of 12, but more than one data set can be entered, but always divisible by 12 - so 12, 24, 36. I do not know how much data will be entered at any given point, but I know it will be divisible by 12.
Sometimes words, sometimes numbers - I do not know in advance what it will be, they are not days, they are not ages, I cannot specify an element to the data points in the array. They will be varying in character length, they will have different punctuation marks in different places in each data set. All of the data is separated by a Tab.
From the list of 12, I need to pull, the 3, 2, 1, 4, 5, 6.
In that order. I then place that data into a List<> and output the data, along with some text boxes to match a specified format.
Purpose of the code:
To loop through the data in sets of 12, until there is no more data.
Each loops pulls a data point from the array, for one example, the 3rd, 15th, 27th, 39th etc.
Each loop adds the data from the words array, adds TextBoxes in various places and then compiles them in a List. The List is then produced for the user to copy into a specified format in a single action.
I thought up the "int Three = 3;" for the purpose of being able to increase that number by 12 each loop. I couldn't think of any other way to do it.
But here to learn, so if you know how it should be done - I am all ears, even if you just want to point me to what I should be doing.
If you need an example of the data, face palm your keyboard 12 times putting a tab between each one...you will have an idea of what the data is - there is absolutely no point me providing an example, it is meaningless gibberish, the only factor that is consistent is the Tab between each grouping.
To long for a comment - Here's a prospective solution implementing what I outlined earlier. It breaks the input into 12 length chunks and creates a temporary collection of word sets that could be used for whatever purpose you need with comments explaining how it works.
void Main()
{
int demoDataSetCount = 10;
int chunkSize=12;
//create some test data as a multiple of the chunk size
var inputData = string.Join(Environment.NewLine, Enumerable.Range(0, demoDataSetCount * chunkSize)
.Select(e => string.Join("\t", Enumerable.Range(e, chunkSize))));
//call the extension method
var wordSets = inputData.GenerateWordSets(chunkSize);
//loop over the resulting collection
foreach(var wordSet in wordSets)
{
//get the various interesting items by index
var three=wordSet.Values.ElementAt(2);
var two=wordSet.Values.ElementAt(1);
var one=wordSet.Values.ElementAt(0);
var four=wordSet.Values.ElementAt(3);
var five=wordSet.Values.ElementAt(4);
var six=wordSet.Values.ElementAt(5);
//write to console demo'ing output
Console.WriteLine($"Index: {wordSet.Index}{Environment.NewLine}\tThree: {three}{Environment.NewLine}\tTwo: {two}{Environment.NewLine}\tOne: {one}{Environment.NewLine}\tFour: {four}{Environment.NewLine}\tFive: {five}{Environment.NewLine}\tSix: {six}");
}
}
//used to hold the intermediate output, one set of words per chunk size
public sealed class WordSet
{
public int Index { get; set; }
public IEnumerable<string> Values { get; set; }
}
public static class Extensions
{
//various optional parameters to tweak behavior
public static IEnumerable<WordSet> GenerateWordSets(this string input, int chunkSize=12, int interestingRangeStart=0, int interestingRangeLength=6)
{
//split the input on new line and tabs
var words = input.Split(new string[] { Environment.NewLine, "\t" }, StringSplitOptions.None);
//generate a range of ints to group with
return Enumerable.Range(0, words.Length)
//int rounding used to group into chunks
//get the words from the current index
.GroupBy(k => k / chunkSize, k=> words[k])
//project grouping into a WordSet collection
.Select(g => new WordSet
{
Index = g.Key,
//take only the indexes we are interested in reading
Values = g.Skip(interestingRangeStart).Take(interestingRangeLength)
});
}
}
See https://stackoverflow.com/a/1008974/426894 for more on how the grouping works here.
Input like this:
0 1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9 10 11 12
2 3 4 5 6 7 8 9 10 11 12 13
....
Generates Output like:
Index: 0
Three: 2
Two: 1
One: 0
Four: 3
Five: 4
Six: 5
Index: 1
Three: 3
Two: 2
One: 1
Four: 4
Five: 5
Six: 6
Index: 2
Three: 4
Two: 3
One: 2
Four: 5
Five: 6
Six: 7
....

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);

Select items based on popularity: Avoiding a glorified sort

I have a site where users can post and vote on suggestions. On the from page I initially list 10 suggestions and the header fetches a new random suggestion every 7 seconds.
I want the votes to influence the probability a suggestion will show up, both on the 10-suggestion list and in the header-suggestion. To that end I have a small algorithm to calculate popularity, taking into account votes, age and a couple other things (needs lots of tweaking).
Anyway, after running the algorithm I have a dictionary of suggestions and popularity index, sorted by popularity:
{ S = Suggestion1, P = 0.86 }
{ S = Suggestion2, P = 0.643 }
{ S = Suggestion3, P = 0.134 }
{ S = Suggestion4, P = 0.07 }
{ S = Suggestion5, P = 0.0 }
{ . . .}
I don't want this to be a glorified sort, so I'd like to introduce some random element to the selection process.
In short, I'd like the popularity to be the probability a suggestion gets picked out of the list.
Having a full list of suggestion/popularity, how do I go about picking 10 out based on probabilities? How can I apply the same to the looping header suggestion?
I'm afraid I don't know how to do this very fast, but if you have the collection in memory you can do it like this:
Note that you do not need to sort the list for this algorithm to work.
First sum up all the probabilities (if the probability is linked to popularity, just sum the popularity numbers, where I assume higher values means higher probability)
Calculate a random number in the range of 0 up to but not including that sum
Start at one end of the list and iterate through it
For each element, if the random number you generated is less than the popularity, pick that element
If not, subtract the popularity of the element from the random number, and continue to the next
If the list is static, you could build ranges and do some binary searches, but if the list keeps changing, then I don't know a better way.
Here is a sample LINQPad program that demonstrates:
void Main()
{
var list = Enumerable.Range(1, 9)
.Select(i => new { V = i, P = i })
.ToArray();
list.Dump("list");
var sum =
(from element in list
select element.P).Sum();
Dictionary<int, int> selected = new Dictionary<int, int>();
foreach (var value in Enumerable.Range(0, sum))
{
var temp = value;
var v = 0;
foreach (var element in list)
{
if (temp < element.P)
{
v = element.V;
break;
}
temp -= element.P;
}
Debug.Assert(v > 0);
if (!selected.ContainsKey(v))
selected[v] = 1;
else
selected[v] += 1;
}
selected.Dump("how many times was each value selected?");
}
Output:
list
[] (9 items)
V P
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
45 45 <-- sum
how many times was each value selected?
Dictionary<Int32,Int32> (9 items)
Key Value
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
45 <-- again, sum

Categories