Get n strings from Linq - c#

I am trying to get every 5 "NewNumber" int's to insert in to var q. Let's say there are 20 records returned by UniqueNumbers, I would like to get 1-5, 6-10, 11-15, 16-20 and then have Number1 = 1,Number2 = 2,Number3 = 3,Number4 = 4,Number5 = 5 passed to var q the first time, followed by Number1 = 6, Number2 = 7, Number3 = 8, Number4 = 9, Number5 = 10 and so on...
var UniqueNumbers =
from t in Numbers
group t by new { t.Id } into g
select new
{
NewNumber = g.Key.Id,
};
UniqueNumbers.Skip(0).Take(5)
var q = new SolrQueryInList("NewNumber1", "NewNumber2","NewNumber3","NewNumber4","NewNumber5");

If you have a list of items, you can easily separate them into groups of five like this:
int count = 0;
var groupsOfFive =
from t in remaining
group t by count++ / 5 into g
select new { Key=g.Key, Numbers = g };
And then:
foreach (var g in groupsOfFive)
{
var parms = g.Numbers.Select(n => n.ToString()).ToArray();
var q = new SolrQueryInList(parms[0], parms[1], parms[2], parms[3], parms[4]);
}
I think what you want is some variation on that.
Edit
Another way to do it, if for some reason you don't want to do the grouping, would be:
var items = remaining.Select(n => n.ToString()).ToArray();
for (int current = 0; current < remaining.Length; remaining += 5)
{
var q = new SolrQueryInList(
items[current],
items[current+1],
items[current+2],
items[current+3],
items[current+4]);
}
Both of these assume that the number of items is evenly divisible by 5. If it's not, you have to handle the possibility of not enough parameters.

Try something like this:
for (int i = 0; i < UniqueNumbers.Count / 5; i++)
{
// Gets the next 5 numbers
var group = UniqueNumbers.Skip(i * 5).Take(5);
// Convert the numbers to strings
var stringNumbers = group.Select(n => n.ToString()).ToList();
// Pass the numbers into the method
var q = new SolrQueryInList(stringNumbers[0], stringNumbers[1], ...
}
You'll have to figure out how to manage boundary conditions, like if UniqueNumbers.Count is not divisible by 5. You might also be able to modify SolrQueryInList to take a list of numbers so that you don't have to index into the list 5 times for that call.
EDIT:
Jim Mischel pointed out that looping over a Skip operation gets expensive fast. Here's a variant that keeps your place, rather than starting at the beginning of the list every time:
var remaining = UniqueNumbers;
while(remaining.Any())
{
// Gets the next 5 numbers
var group = remaining.Take(5);
// Convert the numbers to strings
var stringNumbers = group.Select(n => n.ToString()).ToList();
// Pass the numbers into the method
var q = new SolrQueryInList(stringNumbers[0], stringNumbers[1], ...
// Update the starting spot
remaining = remaining.Skip(5);
}

Related

Determine lower value from a list using LINQ in C#

I have a list with four double values in it
var numbers2 = new List<double>() { 2, 3, 9, 7 };
I need to get lower value between the first 2 indexes (2 and 3).
Similarly I need to get lower value between index 3 and 4 (9 and 7)
Is there a way in C sharp to determine this using LINQ?
Once I have the lower value from above list i.e 2 and 7; I need to pass these values in the below loop
for (int i = 0; i < 1; i++)
{
dac[i] = SetValue(lowerValue[j]);
}
if i == 0, I want lowerValue[j] = 2. If i == 1, I want lowerValue[j] = 7
Well as others have pointed out, it doesn't seem like there's any reason to use linq. But if you absolutely had to find some way to do it, then it's possible. I'll throw 3 options out, the last one being linq.
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var numbers2 = new List<double>() { 2, 3, 9, 7 };
// you stated it's always 4 values. There's no reason to use linq. The optimal solution would
// be a variation of this (with some constant values instead of magic numbers)..
var first = Math.Min(numbers2[0],numbers2[1]);
var second = Math.Min(numbers2[2],numbers2[3]);
Console.WriteLine($"Lower values: {first},{second}");
// if it was an arbitry sized list (but always even count) you could use a iterator method
var listOfLowerValues = ToPairs(numbers2);
var values = string.Join(",", listOfLowerValues.Select(x => x.ToString()));
Console.WriteLine($"Lower values: {values}");
// finally if you absolutely had too, you can make it even more inefficient
// by using linq.
var indexes = Enumerable.Range(0, numbers2.Count);
var indexed = numbers2.Zip(indexes, (n,i) => (index: i, num: n));
var odd = indexed.Where(x => x.index%2 == 0).Select(x => x.num).ToArray();
var even = indexed.Where(x => x.index%2 > 0).Select(x => x.num).ToArray();
var lower = even.Zip(odd,(v1,v2)=> v1 < v2 ? v1 : v2);
var valuesByLinq = string.Join(",",lower.Select(x => x.ToString()));
Console.WriteLine($"Lower values: {valuesByLinq}");
}
static IEnumerable<double> ToPairs(IEnumerable<double> source)
{
int index = 0;
double previous = 0;
foreach(var n in source)
{
if(index++%2 > 0)
{
yield return (previous < n) ? previous : n;
}
else
{
previous = n;
}
}
}
}

Accumulate values of a list

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

Use Linq let sum to calculate an average

How to calculate sum of timedifference and average using linq let method, I tried below mentioned code it's return timedifference list only.
var query1 = from c in DBCollection.Find(Query_Collection).ToList()
let DtCreateDate = Convert.ToDateTime(c["CreatedDate"])
let DtModifiedDate = Convert.ToDateTime(c["LastModifiedDate"])
let difference = (DtModifiedDate - DtCreateDate).TotalSeconds select new { difference };
By doing the following:
var query1 = from c in DBCollection.Find(Query_Collection).ToList()
let DtCreateDate = Convert.ToDateTime(c["CreatedDate"])
let DtModifiedDate = Convert.ToDateTime(c["LastModifiedDate"])
let difference = (DtModifiedDate - DtCreateDate).TotalSeconds
let averageSum = (((DtCreateDate + DtModifiedDate) / 2) + difference) //calculate the average
select new { difference, averageSum };
Above, the 'difference' between two given dates is saved in the variable difference.
I have added another variable called 'averageSum', that now stores the value of the average between the two dates, and then adds the difference to the average.
I've don't understund how you calculate average for every row, but to accumulate custom values you can use Aggregate method as shown below:
var query1 = from c in DBCollection.Find(Query_Collection).ToList()
.Select(e => new {DtCreateDate = Convert.ToDateTime(e["CreatedDate"]), DtModifiedDate = Convert.ToDateTime(e["LastModifiedDate"])})
.Select(e => new {Diff = (e.DtModifiedDate - e.DtCreateDate).TotalSeconds, Av = 0 /* Calculate average for the row */} )
.Aggregate(new {Diff = (double) 0, Av = 0}, (group, cur) => new {Diff = group.Diff + cur.Diff, Av = group.Av + cur.Av});
Result of the statement is a single object which contais sums for difference and average on the whole list

each group by in list with linq

in List<file> i have data :
id Initial B
1 G (2016-27-12)
2 H (2016-27-15)
3 G (2016-27-16)
//my code
List<file> i = new List<file>;
var r = i.Select(i=> i.Initial).GroupBy(x => new { r = x.to List() });
for( int i = 0; i < r.Count(); i++ )
{
comboBox1.Items.Add(r[i].ToString());
}
but my code still error.
how to GroupBy() with linq and each Initial result 2 count value G & H?
You can use
var r = i.Select(i=> i.Initial).GroupBy(x =>x).ToList();
Other way with Distinct()
var r = i.Select(i=> i.Initial).Distinct().ToList();
I dont know if thats what you are trying to do but to me it looks like you want to get every unique Initial from your list. To accomplish that, you can use "Distinct":
var r = i.Select(i=> i.Initial).Distinct();
If this is not what you are trying to do, please provide more info.

want to remove "0" at index 0 in a list but not 10,20,30..... C#

my code
...
List<int> a = new List<int>();
List<int> b = new List<int>();
...
some boxes for input
...
var match = a.Intersect(b);
string output = string.Join(",", match);
foreach (var x in output)
{
label.Text += Regex.Replace(Convert.ToString(x), "[0]", "");
}
but this deletes all 0's
if I enter 0, 10, 100
I only wanna delete the first 0 (index0 as List is sorted) but I can't make an expression saying that it should remove index0 all the time, because i depends on, if the users enter a 0... so it should look for a 0 at first index and (only) if its there... remove it.
I can't wrap my head around this.
Thanks in advance
Don't know why you need regex and the loop over the chars. Here are 3 ways of doing so:
List<int> a = new List<int> { 0, 10, 20, 5 };
List<int> b = new List<int> { 0, 10, 20 };
//Option 1 - .Remove() on one of the lists
a.Remove(0);
var matches = a.Intersect(b);
//Option 2 - .Remove() on intersection
var matches = a.Intersect(b).ToList();
matches.Remove(0);
//Option 3 - .Where()
var matches = a.Where(item => item != 0)
.Intersect(b);
var text = string.Join(",", matches); //For both ways: 10,20

Categories

Resources