How to pass the current index iteration inside a select new MyObject - c#

This is my code:
infoGraphic.chartData = (from x in db.MyDataSource
group x by x.Data.Value.Year into g
select new MyObject
{
index = "", // here I need a string such as "index is:" + index
counter = g.Count()
});
I need the current index iteration inside the select new. Where do I pass it?
EDIT - My current query:
var test = db.MyData
.GroupBy(item => item.Data.Value.Year)
.Select((item, index ) => new ChartData()
{
index = ((double)(3 + index ) / 10).ToString(),
value = item.Count().ToString(),
fill = index.ToString(),
label = item.First().Data.Value.Year.ToString(),
}).ToList();
public class ChartData
{
public string index { get; set; }
public string value { get; set; }
public string fill { get; set; }
public string label { get; set; }
}

Use IEnumerable extension methods, I think the syntax is more straightforward.
You need the 2nd overload, that receives the IEnumerable item and the index.
infoGraphic.chartData.Select((item, index) => {
//what you want to do here
});
You want to apply grouping on your chartData, and afterwards select a subset / generate a projection on the resulting data ?
your solution should look like:
infoGraphic.chartData
.GroupBy(...)
.Select((item, index) => {
//what you want to do here
});
abstracting the dataSource as x:
x.GroupBy(item => item.Data.Value.Year)
.Select((item, index) => new { index = index, counter = item.Count() });
As a follow up to your new question...
here is a simple working scenario with a custom type (like your ChartData):
class Program
{
static void Main(string[] args)
{
List<int> data = new List<int> { 1, 872, -7, 271 ,-3, 7123, -721, -67, 68 ,15 };
IEnumerable<A> result = data
.GroupBy(key => Math.Sign(key))
.Select((item, index) => new A { groupCount = item.Count(), str = item.Where(i => Math.Sign(i) > 0).Count() == 0 ? "negative" : "positive" });
foreach(A a in result)
{
Console.WriteLine(a);
}
}
}
public class A
{
public int groupCount;
public string str;
public override string ToString()
{
return string.Format("Group Count: [{0}], String: [{1}].", groupCount, str);
}
}
/* Output:
* -------
* Group Count: [6], String: positive
* Group Count: [4], String: negative
*/
Important: Make sure the data type you are to use the extension methods is of type IEnumerable (inherits IEnumerable), otherwise you will not find this Select overload my solution is talking about, exposed.

you can do something like this:
let currIndex = collection.IndexOf(collectionItem)
Your code would then become:
infoGraphic.chartData =
(from x in db.MyDataSource group x by x.Data.Value.Year into g
// Get Iterator Index Here
let currIndex = db.MyDataSource.IndexOf(x)
select new MyObject
{index = currIndex.ToString(), // Your Iterator Index
counter = g.Count()
});

Related

How to get Index of IEnumerable SelectList?

I want index from ViewBag and if not possible then From selectedlist but can't get it.
ViewBag.Lst_Fetch_Record = new SelectList(list.OrderBy(ITEM_CD => ITEM_CD));
int index = ViewBag.Lst_Fetch_Record.IndexOf("I1");
Use this method :
ViewBag.Lst_Fetch_Record.Select((item, index) => new
{
...Your Code
}
Getting Index without using for loop
var result2 = new SelectList(list.OrderBy(ITEM_CD=> ITEM_CD)).ToList();
INT Index = result2.IndexOf(result2.Where(p => p.Text == a).FirstOrDefault());
This will give you an ordered list with indexes
List<string> selectListName = new List<String>();
selectListName.Add("Ohio");
selectListName.Add("Maine");
selectListName.Add("Texas");
selectListName.Add("Oregon");
selectListName.Add("Alabama");
var result2 = selectListName.Select( (state, index) => new { index, state
}).OrderBy(a=>a.state).ToList();
foreach(var b in result2)
{
Console.WriteLine( b.index + " " + b.state) ;
}
result: If you re looking for the existing index
4 Alabama
1 Maine
0 Ohio
3 Oregon
2 Texas
To use the result
Console.WriteLine(result2.FirstOrDefault(a=>a.state.Equals("Ohio")).index);
2
Console.WriteLine(result2.FirstOrDefault(a=>a.index.Equals(2)).state);
Ohio
To get this result:
0 Alabama
1 Maine
2 Ohio
3 Oregon
4 Texas
Order first, then index as shown below
var result2 = selectListName.OrderBy(a=>a).Select( (state, index) => new { index,
state }).ToList();
foreach(var b in result2)
{
Console.WriteLine( b.index + " " + b.state) ;
}
Check this example:
class Pet
{
public string Name { get; set; }
public int Age { get; set; }
}
public static void OrderByEx1()
{
Pet[] pets = { new Pet { Name="Barley", Age=8 },
new Pet { Name="Boots", Age=4 },
new Pet { Name="Whiskers", Age=1 } };
IEnumerable<Pet> query = pets.OrderBy(pet => pet.Age);
foreach (Pet pet in query)
{
Console.WriteLine("{0} - {1}", pet.Name, pet.Age);
}
}
Source: Microsoft documentation
https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderby?view=net-5.0

How can I select group by values in specific propoerty?

I am using a group by select using entity framework core and linq.
var list = context.Ways.GroupBY(s=>s.Type).Select(w=> new {
type = w.key,
total = (int)w.Sum(b => b.Length)
})
This giwes me a list.
type total
T1 2541
T2 5481
T5 4
T9 2
T11 856
T3 25
So I want to group into "Others", if total is smaller than 100 like following,
type total
T1 2541
T2 5481
T11 856
OTHERS 31
is this possible?
You can do this with a second group by
var list = context.Ways.GroupBy(s => s.Type).Select(w => new
{
type = w.Key,
total = (int)w.Sum(b => b.Length)
}).GroupBy(s => s.total < 100 ? "Others" : s.type)
.Select(w => new
{
type = w.Key,
total = (int)w.Sum(b => b.total)
});
You can't do this with Entity Framework, but you can write a method to iterate over the list in memory. For example, assuming you have a class to hold the key and value like this (or you could rewrite using KeyValuePair or a tuple):
public class ItemCount
{
public string Name { get; set; }
public int Count { get; set; }
}
An extension method to aggregate the smaller values could look like this:
public static IEnumerable<ItemCount> AggregateWithThreshold(this IEnumerable<ItemCount> source,
int threshold)
{
// The total item to return
var total = new ItemCount
{
Name = "Others",
Count = 0
};
foreach (var item in source)
{
if (item.Count >= threshold)
{
// If count is above threshold, just return the value
yield return item;
}
else
{
// Keep the total count
total.Count += item.Count;
}
}
// No need to return a zero count if all values were above the threshold
if(total.Count > 0)
{
yield return total;
}
}
And you would call it like this:
var list = context.Ways
.GroupBY(s => s.Type)
.Select(w => new ItemCount // Note we are using the new class here
{
Name = w.key,
Count = (int)w.Sum(b => b.Length)
});
var result = list.AggregateWithThreshold(100);
Technically you can add additional operation that would calculate the Others value based on 2 collection values you have already. Like this:
var list = context.Ways.GroupBy(s=>s.Type).Select(w=> new {
type = w.key,
total = (int)w.Sum(b => b.Length)
});
var totalSum = context.Ways.Sum(x => x.Length);
var listSum = list.Sum(x => x.total);
list.Add(new {
type = "Others",
total = totalSum - listSum
});

Find the List index of the object containing the closest property value

How can I find the List index of the object containing the closest property value?
Sample, class MyData contains a property Position. class MyDataHandler has a List of MyData and the positions are: 1, 3, 14, 15, 22.
MyDataHandler has a method called GetClosestIndexAt, If the input value is 13, the method must return index 2.
Sample code:
public class MyData
{
public double Position { get; set; }
public string Name { get; set; }
}
public class MyDataHandler
{
private List<MyData> myDataList = new List<MyData>();
public MyDataHandler()
{
FillMyData(myDataList);
}
public int GetClosestIndexAt(double position)
{
int index = -1;
//How to get the index of the closest MyDataList.Position to position value.
//index = ?????
return index;
}
private void FillMyData(List<MyData> MyDataList)
{
//fill the data...
}
}
You can do it using LINQ, like this:
var res = myDataList
.Select((v, i) => new {Position = v.Position, Index = i}) // Pair up the position and the index
.OrderBy(p => Math.Abs(p.Position - position)) // Order by the distance
.First().Index; // Grab the index of the first item
The idea is to pair the position with its index in the list, order by the distance from the specific position, grab the first item, and get its index.
You need to deal with the situation when there's no elements in myDataList separately. Here is a demo on ideone.
Use overloaded Enumerable.Select method which projects each element of a sequence into a new form by incorporating the element's index:
myDataList.Select((d,i) => new { Position = d.Position, Index = i })
.OrderBy(x => Math.Abs(x.Position - position))
.Select(x => x.Index)
.DefaultIfEmpty(-1) // return -1 if there is no data in myDataList
.First();
Better solution with MinBy operator of MoreLinq (available from NuGet):
public int GetClosestIndexAt(double position)
{
if (!myDataList.Any())
return -1;
return myDataList.Select((d,i) => new { Position = d.Position, Index = i })
.MinBy(x => Math.Abs(x.Position - position))
.Index;
}
You can create your own MinBy extension if you don't want to use library:
public static TSource MinBy<TSource, TKey>(
this IEnumerable<TSource> source, Func<TSource, TKey> selector)
{
using (IEnumerator<TSource> sourceIterator = source.GetEnumerator())
{
if (!sourceIterator.MoveNext())
throw new InvalidOperationException("Empty sequence");
var comparer = Comparer<TKey>.Default;
TSource min = sourceIterator.Current;
TKey minKey = selector(min);
while (sourceIterator.MoveNext())
{
TSource current = sourceIterator.Current;
TKey currentKey = selector(current);
if (comparer.Compare(currentKey, minKey) >= 0)
continue;
min = current;
minKey = currentKey;
}
return min;
}
}
As I said in the comments, I believe the most efficient way is to avoid unnecessary sorting of whole data just to get the first element. We can just select it by searching for the element with minimum difference, calculated separately. It requires two list iteration but no sorting. Given:
var myDataList = new List<MyData>()
{
new MyData() { Name = "Name1", Position = 1.0 },
new MyData() { Name = "Name3", Position = 3.0 },
new MyData() { Name = "Name14", Position = 14.0 },
new MyData() { Name = "Name15", Position = 15.0 },
new MyData() { Name = "Name22", Position = 22.0 },
};
double position = 13.0;
you can write:
var result =
myDataList.Select((md, index) => new
{
Index = index,
Diff = Math.Abs(md.Position - position)
})
.Where(a => a.Diff == myDataList.Min(md => Math.Abs(md.Position - position)))
.First()
.Index;

What is the best way to sort a List of objects with a start value?

What is the best way to sort a List of object with a start value? My list has the items:
obj a
obj b
obj c
obj d
a.index = 1
b.index = 3
c.index = 2
d.index = 4
start value = 3;
sorted list must be {b,d,a,c}
Thanks for help.
Use OrderBy and ThenBy combination. It will work with using System.Linq at top of your file.
var source = new List<Item>() {
new Item { index = 1, value = 'a' },
new Item { index = 3, value = 'b' },
new Item { index = 2, value = 'c' },
new Item { index = 4, value = 'd' },
};
int startValue = 3;
var sortedList = source.OrderBy(i => i.index < startValue)
.ThenBy(i => i.index)
.ToList();
foreach (var item in sortedList)
Console.WriteLine(string.Format("{0} - {1}", item.value, item.index));
Item class definition:
class Item
{
public char value { get; set; }
public int index { get; set; }
}
Returns desired output:
b - 3
d - 4
a - 1
c - 2
If the maximum value is known, you can do your special key function, like:
(item) => { return item.Index < MaxValue? MaxValue + item.Index: item.Index; }
and sort by it. If MaxValue is unknown, you can, well, find it.
Or you can simply sort the list, then split it by StartValue and concatenate parts in reverse order.

Group by same value and contiguous date

var myDic = new SortedDictionary<DateTime,int> ()
{ { new DateTime(0), 0 },
{ new DateTime(1), 1 },
{ new DateTime(2), 1 },
{ new DateTime(3), 0 },
{ new DateTime(4), 0 },
{ new DateTime(5), 2 }
};
How can group these items (with a LINQ request) like this :
group 1 :
startDate: 0, endDate:0, value:0
group 2 :
startDate: 1, endDate:2, value:1
group 3 :
startDate: 3, endDate:4, value:0
group 4 :
startDate: 5, endDate:5, value:2
group are defined by contiguous date and same values.
Is it possible with a groupby ?
Just use a keyGenerating function. This example presumes your dates are already ordered in the source with no gaps.
int currentValue = 0;
int groupCounter = 0;
Func<KeyValuePair<DateTime, int>, int> keyGenerator = kvp =>
{
if (kvp.Value != currentValue)
{
groupCounter += 1;
currentValue = kvp.Value;
}
return groupCounter;
}
List<IGrouping<int, KeyValuePair<DateTime, int>> groups =
myDictionary.GroupBy(keyGenerator).ToList();
It looks like you are trying to group sequential dates over changes in the value. I don't think you should use linq for the grouping. Instead you should use linq to order the dates and iterate over that sorted list to create your groups.
Addition 1
While you may be able to build your collections with by using .Aggregate(). I still think that is the wrong approach.
Does your data have to enter this function as a SortedDictionary?
I'm just guessing, but these are probably records ordered chronologically.
If so, do this:
public class Record
{
public DateTime Date { get; set; }
public int Value { get; set; }
}
public class Grouper
{
public IEnumerable<IEnumerable<Record>> GroupRecords(IEnumerable<Record> sortedRecords)
{
var groupedRecords = new List<List<Record>>();
var recordGroup = new List<Record>();
groupedRecords.Add(recordGroup);
foreach (var record in sortedRecords)
{
if (recordGroup.Count > 0 && recordGroup.First().Value != record.Value)
{
recordGroup = new List<Record>();
groupedRecords.Add(recordGroup);
}
recordGroup.Add(record);
}
return groupedRecords;
}
}

Categories