Alter a months list dynamically - c#

I am stuck in a requirement.
I get two flags from the database, namely lastMonthNumber and lastMonthName, these can range from 1 to 12 and January to December.
Now I have a requirement such that if lastMonthName="March" and lastMonthNumner=12, then the parent list should be as below:
1, April
2, May
3, June
4, July
5, August
6, September
7, October
8, November
9, December
10, January
11, February
12, March
if lastMonthName="April" and lastMonthNumber=6, then the list should be:
7, November
8, December
9, January
10, February
11, March
12, April
This lastMonthNumber can range from 1 to 12 and lastMonthName too can range from Jan to Dec. The parent list needs to be dynamic.
If lastMonthNumber=6 and lastMonthName="April", then the list needs to have 6 elements with April as 12 and backtrack to have total 6 elements.
The parent list can be a dictionary, such as:
var monthsDictionary=new Dictionary<int, string>();
I am trying something as below, but not able to visualize further:
var monthsDictionary = new Dictionary<int, string>();
var numbers = new List<int> { 1,2,3,4,5,6,7,8,9,10,11,12};
var months = new List<string> {"January","February","March","April","May","June","July","August","September","October","November","December" };
foreach (var month in months.Select())
{
if (month == lastMonthName)
{
}
}
Please help. Pointers will be very helpful.

Have a constant with month names
private readonly string[] monthNames = { "Januar" ..., "December" };
and an accessor method that is able to cycle the index:
private string GetMonthName(int index)
{
if (index < 0) return monthNames[monthNames.Length - 1 - (index % monthNames.Length)];
return monthNames[index % monthNames.Length];
}
Then create the list:
int indexOfLastMonthName = Array.IndexOf(monthNames, lastMonthName);
var parentData = Enumerable
// Create as many items as last month number
.Range(0, lastMonthNumber)
// Create the list in reverse order, start on LastMonthName and 12,
// then go back with every item
.Select(x => new
{
Number = 12 - x,
Name = GetMonthName(indexOfLastMonthName - x)
}
// Turn it back
.Reverse();
Then fill it to an appropriate data structure. e.g. like this:
List<Tuple<int, string>> parentList = parentData
.Select(x => Tuple.Create(x.Number, x.Name)))
.ToList();
If you prefer a classic solution (using the GetMonthName method from above):
int indexOfLastMonthName = Array.IndexOf(monthNames, lastMonthName);
List<Tuple<int, string>> parentList = new List<Tuple<int, string>>();
for(int i = 0; i < lastMonthNumber; i++)
{
parentList.Add(
Tuple.Create(
12 - i,
GetMonthName(indexOfLastMonthName - i));
}
parentList.Reverse();

Try this linq query
var months = new List<string> {"January","February","March","April","May","June","July","August","September","October","November","December" };
var lastMonthNumber = ...;
var lastMonthName = ....;
var rows = months.Concat(months)
.Reverse()
.SkipWhile(m => m != lastMonthName)
.Take(lastMonthNumber)
.Reverse()
.Select((m, i) => new { id = i + 12 - lastMonthNumber + 1, m})
.ToArray();
It duplicate a months list, reverse it and skip items until lastMonthName found. After that this code limit result items count to lastMonthNumber and reverse list back

try the below fiddle.
https://dotnetfiddle.net/GYP2Go
private static Dictionary<int, string> GetRequiredResult(int lastMonthNumber, string lastMonthName)
{
var indx = months.IndexOf(lastMonthName);
// here this list will have months in required order that ends with lastMonthName
var revisedMonthList = new List<string>();
revisedMonthList.AddRange(months.Skip(indx + 1).Take(12));
revisedMonthList.AddRange(months.Take(indx + 1));
// get count = lastMonthNumber element from last using index, and then convert them to dictionary.
return revisedMonthList
.Select((mn, index) => new {index, mn})
.Where(c => c.index >= months.Count - lastMonthNumber)
.ToDictionary(c=>c.index + 1, c=>c.mn);
}

var monthsDictionary = new Dictionary<int, string>();
var numbers = new List<int> { 1,2,3,4,5,6,7,8,9,10,11,12};
var months = new List<string> {"January","February","March","April","May","June","July","August","September","October","November","December" };
int flag=0;
int items=12;
var numbersList = new List<int>();
var monthsList = new List<string>();
foreach (var month in months)
{
if(flag==0){
monthsList.Insert(items--,month);
if (month == lastMonthName)
{
flag=1;
}
}
else if(flag==1)
{
monthsList.add(month);
}
}
flag=0;

try following:
var months = Enumerable.Range(1, 12).Select(i => new
{
I = i,
M = System.Globalization.DateTimeFormatInfo.CurrentInfo.GetMonthName(i)
});
//string lastMonthName = "March"; int lastMonthNumber = 12;
//string lastMonthName = "April"; int lastMonthNumber = 6;
var selMonthInt = months.Where(x => x.M == lastMonthName).Select(y => y.I).FirstOrDefault();
int endCount = lastMonthNumber + selMonthInt;
if (endCount >= 12) { endCount = selMonthInt; }
var lst1 = months.Where(x => x.I > endCount).Select(z => z.M);
var lst2 = months.Where(x => x.I <= selMonthInt).Select(z => z.M);
var lst = lst1.Union(lst2).ToArray();
var selMonths = Enumerable.Range(0, lastMonthNumber).Select(i => new { I = (13 - lastMonthNumber + i), M = lst[i] });

Related

Add missing values to a list in xamarin forms

I have a custom object list which has month and another int value. I am getting this list from the database. So data coming from a web service as JSON. Sometimes month is not consecutive. Sometimes some month get missing. For example if this is month 7,
then months in list may contains something like this.
{1,2,3,6,7}
so I want to add the missing months which are 4,5 ----
{1,2,3,4,5,6,7}
other value should be 0 (NEW_REC_COUNT)
My Object class
public class NewData
{
public int MONTH { get; set; }
public int NEW_REC_COUNT { get; set; }
}
Sample Json
[
{
"MONTH": 1,
"NEW_REC_COUNT": 19
},
{
"MONTH": 2,
"NEW_REC_COUNT": 5
},
{
"MONTH": 3,
"NEW_REC_COUNT": 2
},
{
"MONTH": 6,
"NEW_REC_COUNT": 9
},
{
"MONTH": 7,
"NEW_REC_COUNT": 3
}
]
You can try below approach,
Select all months (int value) from list using Select
var months = NewDataList.Select(x => x.MONTH); // This will give you all integers i.e MONTHs.
Find Max() from months and create Range from 1... maxMonths
var maxMonths = months.Max();
var oneTomaxMonths = Enumerable.Range(1,maxMonths).ToList();
Now you have 2 lists i.e. months and oneToMaxMonths, use Except to get missing Months from list of New Data
var results = oneTomaxMonths.Except(months);
Foreach result create new instance with NEW_REC_COUNT = 0
POC : .net Fiddle
If you don't have a lot of data, you can try a smiple loop and Insert omitted items into the list:
List<NewData> list = ...
// If list is not guarantee to be sorted
list.Sort((a, b) => a.MONTH.CompareTo(b.MONTH));
for (int i = 0; i < list.Count - 1; ++i) {
NewData current = list[i];
NewData next = list[i + 1];
// Do we have a hole at position i + 1?
if (current.MONTH + 1 < next.MONTH) {
list.Insert(i + 1, new NewData() {
MONTH = current.MONTH + 1, // Omitted month
NEW_REC_COUNT = 0, // Default value
});
}
}
Edit: If we want months from 1 up and including the current month (DateTime.Today.Month), we can use Linq:
using System.Linq;
...
List<NewData> list = ...
// Let's generalize a bit if you want, say Q3 period
int fromMonth = 1;
int upToMonth = DateTime.Today.Month; // or 7 for testing
list = Enumerable
.Range(fromMonth, upToMonth - fromMonth + 1)
.Select(month =>
list.FirstOrDefault(item => item.MONTH == month)
?? new NewData() { MONTH = month, // Omitted month
NEW_REC_COUNT = 0 }) // Default value
.ToList();
If you want to modify existing list:
list.AddRange(Enumerable
.Range(fromMonth, upToMonth - fromMonth + 1)
.Where(month => !list.Any(item => item.MONTH == month))
.Select(month => new NewData() {
MONTH = month,
NEW_REC_COUNT = 0 })
.ToArray());

Split array into array of arrays

There's an array:
var arr = new int[] { 1, 1, 2, 6, 6, 7, 1, 1, 0 };
Is there a simple way to split it into arrays of the same values?
var arrs = new int[][] {
new int[] { 1, 1 },
new int[] { 2 },
new int[] { 6, 6 },
new int[] { 7 },
new int[] { 1, 1 },
new int[] { 0 } };
I would prefer a linq solution but couldn't find it at the first time.
I would write an extension method for this:
public static class SOExtensions
{
public static IEnumerable<IEnumerable<T>> GroupSequenceWhile<T>(this IEnumerable<T> seq, Func<T, T, bool> condition)
{
List<T> list = new List<T>();
using (var en = seq.GetEnumerator())
{
if (en.MoveNext())
{
var prev = en.Current;
list.Add(en.Current);
while (en.MoveNext())
{
if (condition(prev, en.Current))
{
list.Add(en.Current);
}
else
{
yield return list;
list = new List<T>();
list.Add(en.Current);
}
prev = en.Current;
}
if (list.Any())
yield return list;
}
}
}
}
and use it as
var arr = new int[] { 1, 1, 2, 6, 6, 7, 1, 1, 0 };
var result = arr.GroupSequenceWhile((x, y) => x == y).ToList();
var grouped = arr.GroupBy(x => x).Select(x => x.ToArray())
Didn't notice you were after neighbouring groups initially, the following should work for that
var arr = new[] { 1, 1, 2, 6, 6, 7, 1, 1, 0 };
var groups = new List<int[]>();
for (int i = 0; i < arr.Length; i++)
{
var neighours = arr.Skip(i).TakeWhile(x => arr[i] == x).ToArray();
groups.Add(neighours);
i += neighours.Length-1;
}
Live example
This will do the trick:
var arrs = arr.Select((x, index) =>
{
var ar = arr.Skip(index)
.TakeWhile(a => a == x)
.ToArray();
return ar;
}).Where((x, index) => index == 0 || arr[index - 1] != arr[index]).ToArray();
Basically this will generate an array for each sequence item with a length of 1 or greater and will only choose the arrays which correspond to an item in the original sequence which is either the first element or an element that differs from its predecessor.
You can try this:
int index = 0;
var result = arr.Select(number =>
{
var ar = arr.Skip(index)
.TakeWhile(a => a == number)
.ToArray();
index += ar.Length;
return ar;
}).Where(x => x.Any()).ToArray();
An extension method like the answer by #L.B but a little bit more functional oriented:
public static IEnumerable<IEnumerable<T>> GroupWhile<T>(this IEnumerable<T> source, Func<T, T, bool> func)
{
var firstElement = source.FirstOrDefault();
return firstElement == null ? Enumerable.Empty<IEnumerable<T>>() : source.Skip(1).Aggregate(new
{
current = Tuple.Create(firstElement, ImmutableList<T>.Empty.Add(firstElement)),
results = ImmutableList<ImmutableList<T>>.Empty,
}, (acc, x) =>
func(acc.current.Item1, x)
? new { current = Tuple.Create(x, acc.current.Item2.Add(x)), results = acc.results }
: new { current = Tuple.Create(x, ImmutableList<T>.Empty.Add(x)), results = acc.results.Add(acc.current.Item2) },
x => x.results.Add(x.current.Item2).Select(r => r));
}
Note that the extension method uses the Microsoft Immutable Collections library. The library can be downloaded through NuGet.
Usage:
var arr = new int[] { 1, 1, 2, 6, 6, 7, 1, 1, 0 };
var result = arr.GroupWhile((prev, current) => prev == current);
var printFormattedResult = result.Select((x, i) => Tuple.Create(i, string.Join(",", x)));
foreach (var array in printFormattedResult)
Console.WriteLine("Array {0} = {1}", array.Item1, array.Item2);
Output:
Array 0 = 1,1
Array 1 = 2
Array 2 = 6,6
Array 3 = 7
Array 4 = 1,1
Array 5 = 0
Benchmark
Just for the sake of fun, I tried to benchmark the answers.
I used the following code:
var rnd = new Random();
var arr = Enumerable.Range(0, 100000).Select(x => rnd.Next(10)).ToArray();
var time = Stopwatch.StartNew();
var result = <answer>.ToArray();
Console.WriteLine(t.ElapsedMilliseconds);
And got the following results:
-------------------------------------
| Solution Time(ms) Complexity |
------------------------------------|
| L.B | 3ms | O(n) |
|-----------------------------------|
|´ebb | 41ms | O(n) |
|-----------------------------------|
| James | 137ms | O(n^2) |
|-----------------------------------|
| Robert S. | 155ms | O(n^2) |
|-----------------------------------|
| Selman22 | 155ms | O(n^2) |
-------------------------------------
The slight time overhead from my solution (the 41ms) is due to using immutable collections. Adding an item to ex. List<T> would modify the List<T> object. - Adding an item to ImmutableList<T> clones the current elements in it, and adds them to a new ImmutableList<T> along with the new item (which results in a slight overhead).

Get the next number in a list of numbers using LINQ

I have a list of numbers as below:
var mylist = new List<double> {1, 5, 8, 10, 12};
How can I get the number after a specific number. I want an LINQ expression that receives for example 8 and gives me 10;
You can use the following:
double result = mylist.SkipWhile(n => n != 8).Skip(1).First();
This should also work if you do not have duplicate numbers.
int index = mylist.IndexOf(8);
if (index != -1)
{
double result = index == mylist.Count - 1 ? mylist[index] : mylist[index + 1];
}
This should work,
var mylist = new List<double> { 1, 5, 8, 10, 12 };
double p = 8;
var result = mylist.Where(x => x > p).Take(1).SingleOrDefault();

find elements include a text in List

how i find elements contains a value.
List<int> primes = new List<int>(new int[] { 19, 23,2, 29,23 });
int index = primes.IndexOf(2);
label1.Text = index.ToString();
for (int i = index+1; i < primes.Count; )
{
index = primes.IndexOf(2, i );
label1.Text += "-" + index.ToString();
i = index+1;
}
Output:
1-2-3-4
Something like this?
List<int> primes = new List<int>(new int[] { 19, 23, 2, 29, 23 });
label1.Text = String.Join("-", primes.Select((i, inx) => new { i, inx })
.Where(x => x.i.ToString().Contains("2"))
.Select(x => x.inx));
Label's text will be 1-2-3-4
List<int> primes = new List<int>(new int[] { 19, 23,2, 29,23 });
for (int i = 0; i < primes.Count; i++)
{
if (primes[i].ToString().Contains("2"))
label1.Text = label1.Text += "-" + i;
}
label1.Text = label1.Text.Substring(1, label1.Text.Length - 1); // this line just removes the dath at the start of the label1.Text
this code should do the job but the label1.Text will be 1-2-3-4 cause 2nd 3rt 4th and 5th elements have 2 inside of it or are equal to two
to check list for values
primes.Contains(2)
to know indexes for each element can do
var foo = primes.Select((x, i) => new { x, i }).ToLookup(x => x.x, x => x.i);
this will group elements and their respective indexes

Sort an integer array by given starting integer

I have an array:
int[] months = new int[4] {1, 4, 7, 10};
I would like to sort the array starting by the given value and sort the rest of the array in the original order.
Let's say I want to start sorting the array by a value of 7. The sorted array would be then in order of:
7, 10, 1, 4
Or starting with a value 4 the sorted array would be an order of
4, 7, 10, 1
How about:
var orderedMonths = months.Where(x => x >= 7)
.OrderBy(x => x)
.Concat(months.Where(x => x < 7));
Note that this will mean that the elements of the "rest of the array" will be in order of appearance rather than increasing numeric order. If you meant the latter (i.e. sort both 'segments' numerically) , I would do:
var orderedMonths = months.OrderBy(x => x < 7) // false comes before true
.ThenBy(x => x);
On the other hand, if you want to sort both segments by order of appearance, I would do:
var orderedMonths = months.GroupBy(x => x < 7)
.OrderBy(group => group)
.SelectMany(x => x);
(or)
var orderedMonths = months.Where(x => x >= 7)
.Concat(months.Where(x => x < 7));
Assuming this is your sorted int array you could
int[] months = new int[4] { 1, 4, 7, 10 };
int value = 10;
int[] chk1 = new int[4];
chk1 = months.SkipWhile(a => a != value).
Concat(months.TakeWhile(a => a != value)).ToArray();
This should get you the required order
Can you use a list?
int NumberToBeFound = 7;
int IndexOfNumber = -1;
for(int i=0;i<months.count;i++){
if(months[i] == NumberToBeFound){
IndexOfNumber = i;
break;
}
}
List<int> Sorted = new List<int>();
for(int i = IndexOfNumber; i < months.count;i++){
Sorted.Add(months[i]);
}
for(int i = 0; i < IndexOfNumber; i++){
Sorted.Add(months[i]);
}
months = Sorted.ToArray();

Categories

Resources