I m in a situation where i need to find record from a generic list using its position, means need to find 1st 5th and 9th record , then 2nd 6th and 10th record and so on...
Situation is
A list of projects assigned to a List of Team,
So if we have 20 projects and 4 teams
then 1st project go to 1st team, 2nd go to 2nd team , 3rd go to 3rd team, 4th go to 4th team
then again 5th project go to 1st team
so its like
Projects Team
1 1
2 2
3 3
4 4
5 1
6 2
7 3
8 4
9 1
.
.
so now i want to run a Query on Generic List to get record for each team, so for first team record 1,5 and 9.... need to fetch.
Some thing like
List<Project> lst = list (from Database)
//For 1stTeam
lst = lst.Index(1,5,9...);
//For 2nsTeam
lst = lst.Index(2,6,10...);
Hope i clear my point.
You could do something like this with LINQ Select and GroupBy:
List<int> list = new List<int>{1,2,3,4,5,6,7,8,9,10};
int numberOfTeams = 4;
var projectsByTeam = list
.Select((number, index) => new {Value = number, Index = index})
.GroupBy(item => item.Index % numberOfTeams)
.Select(item => new {TeamNumber = item.Key+1, ProjectIDs = item.Select(x => x.Value).ToList()})
.ToList();
Splits the original list into
{
{TeamNumber = 1, ProjectIDs = {1,5,9}},
{TeamNumber = 2, ProjectIDs = {2,6,10}},
{TeamNumber = 3, ProjectIDs = {3,7}},
{TeamNumber = 4, ProjectIDs = {4,8}},
}
First, this is not specific to generic lists.
You have to create a new list, and then, one by one, add the items from the original list that you want in the new list. You can access single items at a given position via the indexer (square brackets).
List<Project> lst = // list (from Database)
List<Project> firstTeam = new List<Project>();
firstTeam.Add(lst[1]);
firstTeam.Add(lst[5]);
firstTeam.Add(lst[9]);
List<Project> secondTeam = new List<Project>();
secondTeam.Add(lst[2]);
secondTeam.Add(lst[6]);
secondTeam.Add(lst[10]);
Of course, if the items are distributed that regularly throughout the original lst, you can automatically determine the items:
List<Project> firstTeam = new List<Project>();
for (int i = 1; i < lst.Count; i += 4) {
firstTeam.Add(lst[i]);
}
i.e. you loop over the original list, taking every 4th item.
If the items to add to one of the teams are not distributed regularly throughout lst, you will have to add them one by one, but you might be able to make use of the shorter list initializer syntax:
List<Project> firstTeam = new List<Project>() { lst[1], lst[5], lst[9] };
Lastly, note that List<T> starts counting indices at zero, so the very first item is lst[0], not lst[1].
You are looking for the params keyword. It will allow you to pass in to Index an array of arguments, which are the indexes in your case.
In your case an extension method can do the trick:
public static List<Project> Index(this List<Project> list, params int[] indexes)
{
var newList = new List<Project>();
foreach(var index in indexes)
{
newList.Add(list[index]);
}
return newList;
}
// Define other methods and classes here
static IEnumerable<IEnumerable<T>> CustomSplit<T>(this IEnumerable<T> source, int max)
{
var results = new List<List<T>>();
for (int i = 0; i < max; i++)
{
results.Add(new List<T>());
}
int index = 0;
using (var enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
int circularIndex = index % max;
results[circularIndex].Add(enumerator.Current);
index++;
}
}
return results;
}
And here is how to use it:
void Main()
{
var elements = Enumerable.Range(0, 100).CustomSplit(4);
}
You can use:
List<Project> projects; // = something from db
var neededIndexes = new[] { 0, 4, 8 };
var result = projects.Where((project, index) => neededIndexes.Contains(index)).ToList();
Or if the indexes are evenly distributed:
List<Project> projects; // = something from db
var result = projects.Where((project, index) => index % 4 == 0).ToList();
This solve your problem:
List for each team:
List<List<Project>> projectsPerTeam = new List<List<Project>> ();
for(int i=0;i<teamsList.Count();i++)
{
projectsPerTeam.Add(new List<Project> ());
}
Now your issue (add project for correct team):
for(int i=0;i<projectsList.Count();i++)
{
projectsPerTeam[i%teamList.Count()].Add(projectsList[i]);
}
Related
I'm working on aforge. I have a list of data which I Draw on the screen next to blobs. I'm also adding the data to a list box. But Instead of getting added in a left to right sequence, its getting added as per the blob's XY coordinate as shown below in the first listbox.
I tried to sort the list using Linq by the OrderBy method but then it orders the whole list in ascending order. I dont want that, I want the list to be sorted by the first line, then the next line and so on. I tried using take<> to sort it by the first row, but it only sorts the first row of 5 int and then stops.
Code :
int n = 5;
elements = elements.Take(n).OrderBy(i => i).ToList();
foreach (var cogxx in elements)
{
listBox2.Items.Add(cogxx);
}
If List coord = new List{80,90,100,60,70,20,40,30,10,50,} if user input int row is 2 then output should be {60,70,80,90,100,10,20,30,40,50} How can I do this?
If you have no special class representing your Line object, then you can use regex to parse the string. In this case, I use name capture group of Regex:
List<string> elements = new List<string>
{
"Line 1 int 1",
"Line 2 int 1",
"Line 1 int 2",
"Line 1 int 3",
"Line 2 int 2",
"Line 2 int 12",
};
var pattern = #"^\bLine \b(?<num1>\d+) \bint \b(?<num2>\d+)$";
Regex regex = new Regex(pattern);
var query =
from e in elements
where regex.Match(e).Success
orderby
int.Parse(regex.Match(e).Groups["num1"].Value),
int.Parse(regex.Match(e).Groups["num2"].Value)
select e;
var orderedResult = query.ToList();
Or the same with fluent API LINQ:
var orderedResult =
elements
.Where(e => regex.Match(e).Success)
.OrderBy(e => int.Parse(regex.Match(e).Groups["num1"].Value))
.ThenBy(e => int.Parse(regex.Match(e).Groups["num2"].Value))
.ToList();
The orderedResult should be:
Line 1 int 1
Line 1 int 2
Line 1 int 3
Line 2 int 1
Line 2 int 2
Line 2 int 12
UPDATE:
Create a class and extension methods that would split your list into chunks:
public static class MyLinqExtensions
{
public static IEnumerable<IEnumerable<T>> Batch<T>(
this IEnumerable<T> source, int batchSize)
{
using (var enumerator = source.GetEnumerator())
while (enumerator.MoveNext())
yield return YieldBatchElements(enumerator, batchSize - 1);
}
private static IEnumerable<T> YieldBatchElements<T>(
IEnumerator<T> source, int batchSize)
{
yield return source.Current;
for (int i = 0; i < batchSize && source.MoveNext(); i++)
yield return source.Current;
}
}
This code was taken from this answer.
Then you use Batch extension method the following way:
List<int> coord = new List<int> { 80, 90, 100, 60, 70, 20, 40, 30, 10, 50 };
int n = 5;
var orderedResult =
coord.Batch(n)
.Select(b => b.OrderBy(i => i))
.SelectMany(x => x)
.ToList();
If you want to learn LINQ, LINQPad is your friend.
I have a list with that each object has two fields:
Date as DateTime
Estimated as double.
I have some values like this:
01/01/2019 2
01/02/2019 3
01/03/2019 4
... and so.
I need to generate another list, same format, but accumulating the Estimated field, date by date. So the result must be:
01/01/2019 2
01/02/2019 5 (2+3)
01/03/2019 9 (5+4) ... and so.
Right now, I'm calculating it in a foreach statement
for (int iI = 0; iI < SData.TotalDays; iI++)
{
DateTime oCurrent = SData.ProjectStart.AddDays(iI);
oRet.Add(new GraphData(oCurrent, GetProperEstimation(oCurrent)));
}
Then, I can execute a Linq Sum for all the dates prior or equal to the current date:
private static double GetProperEstimation(DateTime pDate)
{
return Data.Where(x => x.Date.Date <= pDate.Date).Sum(x => x.Estimated);
}
It works. But the problem is that is ABSLOUTELLY slow, taking more than 1 minute for a 271 element list.
Is there a better way to do this?
Thanks in advance.
You can write a simple LINQ-like extension method that accumulates values. This version is generalized to allow different input and output types:
static class ExtensionMethods
{
public static IEnumerable<TOut> Accumulate<TIn, TOut>(this IEnumerable<TIn> source, Func<TIn,double> getFunction, Func<TIn,double,TOut> createFunction)
{
double accumulator = 0;
foreach (var item in source)
{
accumulator += getFunction(item);
yield return createFunction(item, accumulator);
}
}
}
Example usage:
public static void Main()
{
var list = new List<Foo>
{
new Foo { Date = new DateTime(2018,1,1), Estimated = 1 },
new Foo { Date = new DateTime(2018,1,2), Estimated = 2 },
new Foo { Date = new DateTime(2018,1,3), Estimated = 3 },
new Foo { Date = new DateTime(2018,1,4), Estimated = 4 },
new Foo { Date = new DateTime(2018,1,5), Estimated = 5 }
};
var accumulatedList = list.Accumulate
(
(item) => item.Estimated, //Given an item, get the value to be summed
(item, sum) => new { Item = item, Sum = sum } //Given an item and the sum, create an output element
);
foreach (var item in accumulatedList)
{
Console.WriteLine("{0:yyyy-MM-dd} {1}", item.Item.Date, item.Sum);
}
}
Output:
2018-01-01 1
2018-01-02 3
2018-01-03 6
2018-01-04 10
2018-01-05 15
This approach will only require one iteration over the set so should perform much better than a series of sums.
Link to DotNetFiddle example
This is exactly job of MoreLinq.Scan
var newModels = list.Scan((x, y) => new MyModel(y.Date, x.Estimated + y.Estimated));
New models will have the values you want.
in (x, y), x is the previous item and y is the current item in the enumeration.
Why your query is slow?
because Where will iterate your collection from the beginning every time you call it. so number of operations grow exponentially 1 + 2 + 3 + ... + n = ((n^2)/2 + n/2).
You can try this. Simple yet effective.
var i = 0;
var result = myList.Select(x => new MyObject
{
Date = x.Date,
Estimated = i = i + x.Estimated
}).ToList();
Edit : try in this way
.Select(x => new GraphData(x.Date, i = i + x.Estimated))
I will assume that what you said is real what you need hehehe
Algorithm
Create a list or array of values based in the original values ordered date asc
sumValues=0;
foreach (var x in collection){
sumValues+= x.Estimated; //this will accumulate all the past values and present value
oRet.Add(x.date, sumValues);
}
The first step (order the values) is the most important. For each will be very fast.
see sort
I have a class that has a bunch of different variables and a couple lists 1 in particular holds ints(positionInts)
I also have a list(teamsList) for holding objects I have created from that class
now I would like to sort the team's list by positions values
Hopefully, I'm not being too vague as the project I'm working on is full of not well-written code so it can be hard to explain.
This function orders the list according to your precondition.
private List<String> OrderList(List<String> teams, int[] positions)
{
List<String> orderedTeams;
Dictionary<int, string> teamsToOrder = new Dictionary<int, string>();
int position = 0;
foreach (string team in teams)
{
teamsToOrder.Add(positions[position], teams[position]);
position = position + 1;
}
orderedTeams = teamsToOrder.OrderByDescending(team => team.Key).Select(team => team.Value).ToList();
return orderedTeams;
}
If I understand your question correctly, then you have list of arbitrary type, for example list of strings:
var teamsList = new List<String> { "team1", "team2", "team3", "team4" };
Next up, you have enumeration of integers:
var positionInts = new[] { 2, 3, 1, 0 };
And your goal is to order teamsList based on sequence numbers of the positionInts. In that case you can use following method:
static IEnumerable<T> OrderBySequence<T>(IEnumerable<T> source, IEnumerable<Int32> sequence)
{
for (var i = 0; i < Math.Min(source.Count(), sequence.Count()); i++)
{
var s = sequence.ElementAt(i);
if (s > -1 && s < source.Count())
{
yield return source.ElementAt(s);
}
}
}
Which will produce:
team3
team4
team2
team1
So here a problem which i am facing -
I have two lists with following structure
public class Payment
{
public int Period { get; set; }
public decimal Balance{ get; set; }
}
I have created following two lists as below
List<Payment> A = new List<Payment>();
List<Payment> B = new List<Payment>();
The list looks like this.
List A List B
Perid Payment Perid Payment
1 10 1 16
2 12 2 13
3 45 3 44
4 23 4 33
5 36 5 34
6 45 6 35
I am trying to add these two Payments from list A,B and create a third list which should have same structure.
List C
Perid Payment
1 10+16
2 12+13
3 45+44
4 23+33
5 36+34
6 45+35
I understand with for looping its possible but is there anyway where Linq OR Lambda expressions can be used in simpler way?
Any help is deeply appreciated.
Try LINQ's Zip method. It helps you to iterate over two collections simultaneously.
Here's an example -
using System;
using System.Linq;
class Program
{
static void Main()
{
// Two source arrays.
var array1 = new int[] { 1, 2, 3, 4, 5 };
var array2 = new int[] { 6, 7, 8, 9, 10 };
// Add elements at each position together.
var zip = array1.Zip(array2, (a, b) => (a + b));
// Look at results.
foreach (var value in zip)
{
Console.WriteLine(value);
}
}
}
I think you shouldn't do it. Write the code in the old-fashioned way is going to be cleared to almost anybody reading the code.
More importantly, the non-LINQ code will allow you to add sanity checks in a reasonable fashion (for example, are you sure all periods in the first list exist in the second? And vice versa?).
If you want to get more modern, I suggest using a generator, something like this:
IEnumerable<Payment> UnitePayments(List<Payment> list1, List<Payment> list2)
{
... Check that list1 and list2 are the same length ...
for(int i=0; i<list1.Length; i++)
{
if(list1.Period!=list2.Period) ... handle this case...
yield return new Payment { Period = list1.Period,
Balance = list1.Balance + list2.Balance };
}
}
Your code readers will thank you.
You have two options as already suggested:-
Using Concat + GroupBy :-
List<Payment> result = A.Concat(B).GroupBy(x => x.Period)
.Select(x => new Payment
{
Period = x.Key,
Balance = x.Sum(z => z.Balance)
}).ToList();
Using Zip :-
List<Payment> result1 = A.Zip(B, (first, second) => new Payment
{
Period = first.Period,
Balance = first.Balance + second.Balance
}).ToList();
You can refer to this Fiddle.
// Try for loop i think it would be good way to handle this situation there is other LINQ queries but i believe this is easier..
List<int> a = new List<int>();
a.Add(1 ) ;
a.Add(2);
List<int> b = new List<int>();
b.Add(5) ;
b.Add(6);
List<int> c = new List<int>();
for (int x = 0; x < a.Count; x++)
{
c.Add(a[x] + b[x]);
Label1.Text += c[x] + "";
}
I have a arraylist which has values some of them are repeated. I need the count of the repeated values. Is this possible in c#?
here is a great post how to do it with LINQ
var query =
from c in arrayList
group c by c into g
where g.Count() > 1
select new { Item = g.Key, ItemCount = g.Count()};
foreach (var item in query)
{
Console.WriteLine("Country {0} has {1} cities", item.Item , item.ItemCount );
}
If your object has equals method correctly overrided just call Distinct() from System.Linq namespace on it
It requires the ArrayList to be homogeneous and calling Cast<YourType>() before Distinct().
Then subtract the length of arrayList from the Distinct sequence.
arraList.Count - arraList.Cast<YourType>().Distinct().Count()
it will throw exception if your items in arrayList is not of type YourType, and if you use OfType<YourType> it filters items to objects of type YourType.
but if you want the count of each repeated item, this is not your answer.
public Dictionary<T,int> CountOccurences<T>(IEnumerable<T> items) {
var occurences = new Dictionary<T,int>();
foreach(T item in items) {
if(occurences.ContainsKey(item)) {
occurences[item]++;
} else {
occurences.Add(item, 1);
}
}
return occurences;
}
myList.GroupBy(i => i).Count(g => g.Count() > 1)
and if you specifically need ArrayList
ArrayList arrayList = new ArrayList(new[] { 1, 1, 2, 3, 4, 4 });
Console.WriteLine(arrayList.ToArray().GroupBy(i => i).Count(g => g.Count() > 1));
Based on comments by poster
ArrayList arrayList = new ArrayList(new[] { 1, 1, 2, 3, 4, 4 });
Console.WriteLine(arrayList.ToArray().Count(i => i == 4));
int countDup = ArrayList1.Count - ArrayList1.OfType<object>().Distinct().Count();
var items = arrayList.Cast<object>()
.GroupBy(o => o)
.Select(g => new { Item = g, Count = g.Count() })
.ToList();
each item of result list will have two properties:
Item - source item
Count - count in source list
You can acomplish this many ways. The first that comes to me would be to group by the values within your array list, and only return the grouping counts that are over 1.
ArrayList al = new ArrayList();
al.Add("a");
al.Add("b");
al.Add("c");
al.Add("f");
al.Add("a");
al.Add("f");
int count = al.ToArray().GroupBy(q => q).Count(q=>q.Count()>1);
count will return the value of 2 as a and f are duplicated.
You could sort it, then it becomes very easy.
Edit: sorting becomes a moot point when done this way.
Arraylist myList = new ArrayList();
myList = someStuff;
Dictionary<object, int> counts = new Dictionary<object,int>();
foreach (object item in myList)
{
if (!counts.ContainsKey(item))
{
counts.Add(item,1);
}
else
{
counts[item]++;
}
}
Edit:
Some minor things might vary (not certain about some of my square braces, I'm a little rusty with c#) but the concept should withstand scrutiny.