I need return sum of elements with odd indexes in the array of doubles
This is my code:
public static double EvaluateSumOfElementsOddPositions(double[] inputData)
{
var sum = inputData
.Select((v, i) => new { Group = (i % 2 != 0), Value = v })
.GroupBy(x => x.Group)
.Select(g => g.Sum(y => y.Value));
return sum ;
}
But I have a mistake:
Can't implicitly convert IEnumerable to double.
I don't know how I can deal with that... Help, please!
What you are looking for is something like:
public static double EvaluateSumOfElementsOddPositions(double[] inputData)
{
return inputData
.Where((data, index) => index % 2 != 0)
.Sum();
}
You don't need to group the elements if you are not going to use elements at even indices.
Although it is a nice exercise to try to do this using LINQ, it is not very efficient.
GroupBy will create a Dictionary<Tkey, ICollection<TResult>>, or to be more precise: a Lookup Table. For every element, it will extract the key and the TResult. For ever element it will check if the Key is in the Lookup Table.
If not, it will put the TResult in a new ICollection<TResult> and add the Key and the collection to the table.
It the Key is in the table it will add the TResult to the end of the collection.
This is quite a lot of work, while in fact the only thing you want is:
public static IEnumerable<double> ToEveryOtherDouble(this IEnumerable<double> doubles)
{
bool everyOther = false;
// will return elements with index 1, 3, 5, ...
// start with true if you want 0, 2, 4, ...
foreach (double d in doubles)
{
if (everyOther)
yield return d;
everyOther = !everyOther;
}
Usage:
IEnumerable<double> inputData = ....
double sum = inputData.ToEveryOtherDouble().Sum();
If you insist on using LINQ, make two groups: a group containing doubles with the even indexes, and a group containing doubles with the odd indexes.
So Key of the group: i % 2
double sum = inputData.Select( (d, index) => new
{
Index = index,
Value = d,
})
.GroupBy(x => x.Index % 2, // keySelector: groups with key 0 and key 1
// parameter elementSelector: specify the elements of each group
x => x.Value) // as elements of the group, we only need the double
Result of the GroupBy: two groups. A group with key 0 and a group with key 1. The group with key 0 has as elements the doubles at even indexes, the group with key 1 has as elements the doubles at odd indexes.
Continuing the LINQ: if you want only the even indexes:
.Where(group => group.Key == 0).Sum();
Conclusion
The choice is yours: which one is easier to read, reuse, maintain and unit test:
double sum = inputData.Select( (d, index) => new
{
Index = index,
Value = d,
})
.GroupBy(x => x.Index % 2, x => x.Value)
.Where(group => group.Key == 0)
.Sum();
Or:
double sum = inputData.ToEveryOtherDouble().Sum();
Related
I'm new to lambda expressions but I think they look great in code, but I'm having problems understanding how to convert a foreach loop into a lambda. I can't understand the other examples I've seen here.
The code I'm trying to convert is:
{
var a = arr.ToList();
var b = a.Distinct().ToList();
if(a.SequenceEqual(b)){return -1;}
foreach(int i in b)
{
a.Remove(i);
}
return a.First();
}
Specifically the
foreach(int i in b)
{
a.Remove(i);
}
Also as I'm here, is if(a.SequenceEqual(b)){return -1;} an okay thing to do? I felt bad using like 4 lines when it could be only 1.
If the goal is to find the first item in the collection that has a duplicate, or to return -1 when there are no duplicates, then it can be done with two lines
int FindFirstDuplicate(IEnumerable<int> arr)
{
HashSet<int> seen = new();
return arr.SkipWhile(e => seen.Add(e)).DefaultIfEmpty(-1).First();
}
But these two lines cannot be written as expression-bodied function.
If there is a goal to write expression-bodied function then the easiest the way is to use GroupBy
int FindFirstDuplicate(IEnumerable<int> arr) =>
arr.GroupBy(e => e)
.Where(g => g.Count() > 1)
.Select(g => g.First())
.DefaultIfEmpty(-1)
.First();
But this option has a very suboptimal memory consumption.
If collection always has a very small number of elements then function can be written without GroupBy
int FindFirstDuplicate(IEnumerable<int> arr) =>
arr.SkipWhile((e, i) => arr.Skip(i + 1).All(x => x != e))
.DefaultIfEmpty(-1)
.First();
But such implementation has quadratic complexity, as it has nested loop. So, it will be extremely slow for thousands records.
There is one more option that has similar performance and memory consumption as the very first function.
int FindFirstDuplicate(IEnumerable<int> arr) =>
Enumerable.Repeat(new HashSet<int>(), arr.Count())
.Zip(arr)
.SkipWhile(e => e.First.Add(e.Second))
.Select(e => e.Second)
.DefaultIfEmpty(-1)
.First();
Repeat and Zip are needed to pass the HashSet object to SkipWhile method.
And the most straightforward way with foreach loop for comparison with other options
int FindFirstDuplicate(IEnumerable<int> arr)
{
HashSet<int> seen = new();
foreach (int item in arr)
{
if (!seen.Add(item))
{
return item;
}
}
return -1;
}
Just to restate what your code does in English: you what to remove 1 of each unique value from a list, leaving duplicate values in place with the order unchanged.
For example if your input list was: [ 4, 7, 1, 3, 7, 4, 8, 7 ] you expect the [contents of a] to be [ 7, 4, 7 ], and therefore the result would be 7 (<-- edited thanks to mjwills).
This is probably bad form because it uses a mutable accumulator, but it would work:
arr.Aggregate(
new { Seen = new HashSet<Int32>(), Result = new List<Int32>() },
(acc, val) => {
if (!acc.Seen.Add(val)) {
// If we've seen this value before, add it to the list
acc.Result.Add(val);
}
return acc;
}).Result
Obviously this does not remove elements from the original array, which is because it is not valid to remove elements from an array while you are using an enumerator with it.
If you don't care about the original order of the items then this is super simple:
List<string> result = arr.GroupBy(x => x).SelectMany(xs => xs.Skip(1)).ToList();
Done. That removes one of each instance from the list.
If the original order is important then this is what you need:
List<string> result =
arr
.Select((x, n) => new { x, n })
.GroupBy(y => y.x)
.SelectMany(ys => ys.Skip(1))
.OrderBy(y => y.n)
.Select(y => y.x)
.ToList();
If I run that on var arr = new string[] { "x", "y", "z", "z", "x", }; then I get "z", "x" as expected.
Well, I think Except operator is more suitable here:
return a.Except(b).FirstOrDefault(-1);
Try this-
a.GroupBy(v => v).Where(c => c.Count() > 1).FirstOrDefault().Key;
I'm trying to remove matching items from a list, which seems like a pretty simple task, but as luck would have it, I can't figure it out.
Example list:
List<int> points1 = new List<int>
{
1, 2, 3, 3
};
I'm trying to get uniquePoints1 to be 1,2
I know there is .Distinct() but that would return 1,2,3 which is not what I want.
I've also tried the following along with .Distinct() but I get a red line saying Comparison made to the same variable, did you mean to compare to something else?
List<int> uniquePoints1 = points1.Where(x => x == x);
List<int> uniquePoints1 = points1.RemoveAll(x => x == x);
Any help or direction is appreciated.
You can use the GroupBy method to group the items, and then return only the numbers from groups that have a count of 1:
List<int> uniquePoints = points
.GroupBy(x => x) // Group all the numbers
.Where(g => g.Count() == 1) // Filter on only groups that have one item
.Select(g => g.Key) // Return the group's key (which is the number)
.ToList();
// uniquePoints = { 1, 2 }
"Group by" to the rescue!
This is a LINQ variant -- see other answers for a non-LINQ version
var nonDuplicatedPoints = from p in points1
group p by p into g
where g.Count() == 1
select g.Key;
I want to access the first, second, third elements in a list. I can use built in .First() method for accessing first element.
My code is as follows:
Dictionary<int, Tuple<int, int>> pList = new Dictionary<int, Tuple<int, int>>();
var categoryGroups = pList.Values.GroupBy(t => t.Item1);
var highestCount = categoryGroups
.OrderByDescending(g => g.Count())
.Select(g => new { Category = g.Key, Count = g.Count() })
.First();
var 2ndHighestCount = categoryGroups
.OrderByDescending(g => g.Count())
.Select(g => new { Category = g.Key, Count = g.Count() })
.GetNth(1);
var 3rdHighestCount = categoryGroups
.OrderByDescending(g => g.Count())
.Select(g => new { Category = g.Key, Count = g.Count() })
.GetNth(2);
twObjClus.WriteLine("--------------------Cluster Label------------------");
twObjClus.WriteLine("\n");
twObjClus.WriteLine("Category:{0} Count:{1}",
highestCount.Category, highestCount.Count);
twObjClus.WriteLine("\n");
twObjClus.WriteLine("Category:{0} Count:{1}",
2ndHighestCount.Category, 2ndHighestCount.Count);
// Error here i.e. "Can't use 2ndHighestCount.Category here"
twObjClus.WriteLine("\n");
twObjClus.WriteLine("Category:{0} Count:{1}",
3rdHighestCount.Category, 3rdHighestCount.Count);
// Error here i.e. "Can't use 3rdHighestCount.Category here"
twObjClus.WriteLine("\n");
I have written extension method GetNth() as:
public static IEnumerable<T> GetNth<T>(this IEnumerable<T> list, int n)
{
if (n < 0)
throw new ArgumentOutOfRangeException("n");
if (n > 0){
int c = 0;
foreach (var e in list){
if (c % n == 0)
yield return e;
c++;
}
}
}
Can I write extension methods as .Second(), .Third() similar to
built in method .First() to access second and third indices?
If what you're looking for is a single object, you don't need to write it yourself, because a built-in method for that already exists.
foo.ElementAt(1)
will get you the second element, etc. It works similarly to First and returns a single object.
Your GetNth method seems to be returning every Nth element, instead of just the element at index N. I'm assuming that's not what you want since you said you wanted something similar to First.
Since #Eser gave up and doesn't want to post the correct way as an answer, here goes:
You should rather do the transforms once, collect the results into an array, and then get the three elements from that. The way you're doing it right now results in code duplication as well as grouping and ordering being done multiple times, which is inefficient.
var highestCounts = pList.Values
.GroupBy(t => t.Item1)
.OrderByDescending(g => g.Count())
.Select(g => new { Category = g.Key, Count = g.Count() })
.Take(3)
.ToArray();
// highestCounts[0] is the first count
// highestCounts[1] is the second
// highestCounts[2] is the third
// make sure to handle cases where there are less than 3 items!
As an FYI, if you some day need just the Nth value and not the top three, you can use .ElementAt to access values at an arbitrary index.
I've checked many solutions on different sites but couldn't find what I was looking for. I'm working on a dictionary object with different Values against Keys. The structure is as follows:
Key Value
6 4
3 4
2 2
1 1
If they dictionary contains elements like this, the output should be 6 and 3, if Key (6) has the highest value, it should print only 6. However, if all the values are same against each key, it should print all the keys.
Trying to use the following but it only prints the highest Value.
var Keys_ = dicCommon.GroupBy(x => x.Value).Max(p => p.Key);
Any ideas
Instead of using Max(x=>x.Key) use .OrderByDescending(x=>x.Key) and .FirstOrDefault() that will give you the group that has the max value. You then can itterate over the group and display whatever you need.
var dicCommon = new Dictionary<int, int>();
dicCommon.Add(6, 4);
dicCommon.Add(3, 4);
dicCommon.Add(2, 2);
dicCommon.Add(1, 1);
var maxGroup = dicCommon.GroupBy(x => x.Value).OrderByDescending(x => x.Key).FirstOrDefault();
foreach (var keyValuePair in maxGroup)
{
Console.WriteLine("Key: {0}, Value {1}", keyValuePair.Key, keyValuePair.Value);
}
Run Code
First off a query can't return one and more than one result at the same time.So you need to pick one.
In this case if you want all Keys that has the highest corresponding Value, you can sort the groups based on Value then just get the first group which has the highest Value:
var Keys_ = dicCommon.GroupBy(x => x.Value)
.OrderByDescending(g => g.Key)
.First()
.Select(x => x.Key)
.ToList();
var keys = String.Join(",", dicCommon
.OrderByDescending(x=>x.Value)
.GroupBy(x => x.Value)
.First()
.Select(x=>x.Key));
You’re almost there:
dicCommon.GroupBy(x => x.Value)
.OrderByDescending(pair => pair.First().Value)
.First().Select(pair => pair.Key).ToList()
GroupBy returns an enumerable of IGrouping. So sort these descending by value, then get the first, and select the key of each containing element.
Since this requires sorting, the runtime complexity is not linear, although we can easily do that. One way would be figuring out the maximum value first and then getting all the keys where the value is equal to that:
int maxValue = dicCommon.Max(x => x.Value);
List<int> maxKeys = dicCommon.Where(x => x.Value == maxValue).Select(x => x.Key).ToList();
I have a List<bool>. I need to get the indexes of top n items where item value = true.
For example the following list items(bool)
10011001000
TopTrueIndexes(3) = The first 3 indexes where bits are true are 0, 3, 4
TopTrueIndexes(4) = The first 4 indexes where bits are true are 0, 3, 4, 7
How can I write a lambda for this?
Well, assuming you have some easily-identifiable condition, you can do something like this, which will work for any IEnumerable<T>:
var query = source.Select((value, index) => new { value, index })
.Where(x => x.value => Condition(value))
.Select(x => x.index)
.Take(n);
(Obviously fill in the appropriate bit of the Where clause. If it's just a List<bool> it may just be x => x.value.)
The important bits are that you use the overload of Select to get index/value pairs before the Where, and then another Select to get just the indexes after the Where... and use Take to only get the first n results.
There's an overload of Select where the lambda gets two parameters: the index and the element. So you can just take the indices where the value is true, supplying a sentinel (here, -1) for the ones you don't want. Then filter out the sentinels and take how many you want:
bool[] bools = ...;
var indices = bools.Select((val, ix) => val ? ix : -1).Where(i => i >= 0).Take(n);
This should probably do it.
IEnumerable<bool> GetItemsInList(IEnumerable<bool> list, int count) {
int ind = 0;
return list.Select(itm => new {i = ind++, v = itm}).Where(itm => itm.v).Take(count).Select(itm => itm.i);
}
Here how it goes:
Select source + its index
Add condition to the where clause (the source of the where clause now contains the original source + index)
Select the index (the index returned here is the original index from the original source)
var indexes = inputList.Select((input, index) => new { input, index }).Where(a => condition(a.input)).Select(a => a.index);