I have the following array of coordinates:
double[] points = { 1, 2, 3, 4, 5, 6 };
Then I have the following class:
public class clsPoint
{
public double X { get; set; }
public double Y { get; set; }
}
I need to copy the points into List objects. Where the first point in the array is the X and the second point in the array is the Y. Here is what I have so far but it is not correct:
List<clsPoint> lstPoints = points
.Select(coord => new clsPoint
{
X = coord[0],
Y = coord[1]
}).ToList();
Expected Results
clsPoint Objects List (lstPoints)
X = 1 , Y = 2
X = 3 , Y = 4
X = 5 , Y = 6
Any help would be appreciated. Thanks.
You can generate a sequence of consecutive values until the half your array, then you can project using those values as index to get the pairs.
var result=Enumerable.Range(0, points.Length / 2).Select(i=>new clsPoint{X=points[2*i],Y=points[2*i+1]});
Update
This is another solution using Zip extension method and one overload of Where extension method to get the index:
var r2 = points.Where((e, i) => i % 2 == 0)
.Zip(points.Where((e, i) => i % 2 != 0), (a, b) => new clsPoint{X= a, Y= b });
I think there is probably a better way for you to compose your points prior to feeding them into your class. A simple for loop may suffice better in this situation as well.
However, in LINQ, you would first use a projection to gather the index so that you could group based on pairs and then use a second projection from the grouping to populate the class.
It looks like this
points.Select((v,i) => new {
val = v,
i = i
}).GroupBy(o => o.i%2 != 0 ? o.i-1 : o.i).Select(g => new clsPoint() {
X = g.First().val,
Y = g.Last().val
});
Using the overload of Select that receives the current index you can set a grouping rule (in this case a different id for each 2 numbers), then group by it and eventually create your new clsPoint:
double[] points = { 1, 2, 3, 4, 5, 6 };
var result = points.Select((item, index) => new { item, index = index / 2 })
.GroupBy(item => item.index, item => item.item)
.Select(group => new clsPoint { X = group.First(), Y = group.Last() })
.ToList();
Doing it with a simple for loop would look like:
List<clsPoint> result = new List<clsPoint>();
for (int i = 0; i < points.Length; i += 2)
{
result.Add(new clsPoint { X = points[i], Y = points.ElementAtOrDefault(i+1) });
}
Related
I have a n-by-3 array I wish to convert to a Dictionary<string,string[]> where the first column is the key and the rest of the column as an array for the value.
For example:
Key = arr[0,0], Value = new string[2] {arr[0,1], arr[0,2]}.
I'm aware of ToDictionary but I don't know how to set the value part.
arr.ToDictionary(x=>arr[x,0],x=>new string[2]{arr[x,1],arr[x,2]});
//This doesn't work!!!
How can I set it up correctly?
Multidimensional arrays are a continuous block of memory, so you kind of have to treat them like a single array. Try this:
var dict = arr.Cast<string>()
.Select((s, i) => new { s, i })
.GroupBy(s => s.i / arr.GetLength(1))
.ToDictionary(
g => g.First().s,
g => g.Skip(1).Select(i => i.s).ToArray()
);
With explanations:
// First, cast it to an IEnumerable<string>
var dict = arr.Cast<string>()
// Use the Select overload that lets us get the index of the element,
// And we capture the element's index (i), along with the element itself (s)
// and put them together into an anonymous type [1]
.Select((s, i) => new { s, i })
// .GetLength(dimension) is a method on multidimensional arrays to
// get the length of a given dimension (pretty self-explanatory)
// In this case, we want the second dimension, or how wide each
// row is: [x,y] <- we want y
// Divide the element index (s.i) by that length to get the row index
// for that element
.GroupBy(s => s.i / arr.GetLength(1))
// Now we have an Grouping<int, IEnumerable<anonymous{string,int}>>
.ToDictionary(
// We don't care about the key, since it's the row index, what we want
// is the string value (the `s` property) from first element in the row
g => g.First().s,
// For the value, we want to skip the first element, and extract
// the string values (the `s` property), and then convert to an array
g => g.Skip(1).Select(i => i.s).ToArray()
);
[1]: See here for documentation on anonymous types.
Sometimes not using linq is easier to read and faster:
var dict = new Dictionary<string, string[]>();
for (int i = 0; i < arr.GetLength(0); i++)
dict[arr[i, 0]] = new string[] { arr[i, 1], arr[i, 2] };
But when you feel like you REALLY need to use linq:
Enumerable.Range(0, arr.GetLength(0))
.ToDictionary(i => arr[i, 0], i => new string[] {arr[i, 1], arr[i, 2]});
This is the simplest approach I can come up with:
var arr = new int[4, 3]
{
{ 1, 2, 3 },
{ 3, 5, 7 },
{ 5, 8, 11 },
{ 7, 11, 15 },
};
var dict = arr.Cast<int>().Buffer(3).ToDictionary(x => x[0], x => x.Skip(1).ToArray());
That gives me:
You just need to NuGet "System.Interactive" to get the Buffer operator.
Or use this implementation:
public static IEnumerable<T[]> Buffer<T>(this IEnumerable<T> source, int count)
=>
source
.Select((t, i) => new { t, i })
.GroupBy(x => x.i / count)
.Select(x => x.Select(y => y.t).ToArray());
I guess your approach was right ,my only doubt is that your array is static or not?
arr.Select((value, index) => new { value, index }) .ToDictionary(x => x.index, x => new string[2]{x.value[x.index][1],x.value[x.index][2]}));
Note: I couldn't execute and check the code ! Sorry.
I had done using Integer. Please Change for your requirements.
public static void Main()
{
int row=0 , col=0;
int[,] array = new int[,]
{
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 },
{ 10, 11, 12 }
};
int flag=0;
for (int i = 0; i < array.Rank; i++)
{
if(flag==0)
{
row= array.GetLength(i);
flag=1;
}
else
{
col= array.GetLength(i);
}
}
Dictionary<int,int[,]> dictionary = new Dictionary<int, int[,]>();
for(int i=0;i<row;i++)
{
dictionary.Add(array[i,0],new int[, ]{{array[i,1]},{array[i,2]}});
}
Console.WriteLine(dictionary[4].GetValue(0,0));
}
Mylist.GroupBy(x => new{x.X, x.Y}).Select(g => g.First()).ToList<XYZ>();
The above code works fine for me. I only want to compare the points based on the round(5) of the point component.
For example x.X = 16.838974347323224 should be only compared as x.X = 16.83897 because I experienced some inaccuracy after the round 5. Any suggestions?
Solution:
Mylist.GroupBy(x => new { X = Math.Round(x.X,5), Y = Math.Round(x.Y,5) })
.Select(g => g.First()).ToList();
Using Round can create a situation where two numbers, even though incredibly close to each other, can end up being considered distinct.
Take this example:
var Mylist = new []
{
new { X = 1.0000051, Y = 1.0 },
new { X = 1.0000049, Y = 1.0 },
new { X = 1.1, Y = 1.0 },
new { X = 1.0, Y = 1.005 },
};
The first two values are very close - in fact they differ in the 6th decimal place.
By what if we run this code:
var result =
Mylist
.GroupBy(x => new
{
X = Math.Round(x.X,5, MidpointRounding.AwayFromZero),
Y = Math.Round(x.Y,5, MidpointRounding.AwayFromZero)
})
.Select(g => g.First())
.ToList();
The result is:
The rounding has allowed these two values to be kept.
The correct approach is to filter by distance. If a subsequent value is within a threshold of the previous values it should be discarded.
Here's the code that does that:
var threshold = 0.000001;
Func<double, double, double, double, double> distance
= (x0, y0, x1, y1) =>
Math.Sqrt(Math.Pow(x1 - x0, 2.0) + Math.Pow(y1 - y0, 2.0));
var result = Mylist.Skip(1).Aggregate(Mylist.Take(1).ToList(), (xys, xy) =>
{
if (xys.All(xy2 => distance(xy.X, xy.Y, xy2.X, xy2.Y) >= threshold))
{
xys.Add(xy);
}
return xys;
});
Now if we run that on the Mylist data we get this:
This is a better ideal for removing duplicates.
To do so use Math.Round:
var result = Mylist.GroupBy(x => new { X = Math.Round(x.X,5, MidpointRounding.AwayFromZero), Y = Math.Round(x.Y,5, MidpointRounding.AwayFromZero) })
.Select(g => g.First()).ToList();
However if what you want is to remove duplicates then instead of GroupBy go for one of these:
Select rounded and then Distinct:
var result = Mylist.Select(item => new XYZ { X = Math.Round(item.X,5, MidpointRounding.AwayFromZero),
Y = Math.Round(item.Y,5, MidpointRounding.AwayFromZero)})
.Distinct().ToList();
Distinct and override Equals and GetHashCode - (equals will do the rounding) - wouldn't suggest
Distinct and implement a custom IEqualityComparer:
public class RoundedXyzComparer : IEqualityComparer<XYZ>
{
public int RoundingDigits { get; set; }
public RoundedXyzComparer(int roundingDigits)
{
RoundingDigits = roundingDigits;
}
public bool Equals(XYZ x, XYZ y)
{
return Math.Round(x.X, RoundingDigits, MidpointRounding.AwayFromZero) == Math.Round(y.X, RoundingDigits, MidpointRounding.AwayFromZero) &&
Math.Round(x.Y,RoundingDigits, MidpointRounding.AwayFromZero) == Math.Round(y.Y, RoundingDigits, MidpointRounding.AwayFromZero);
}
public int GetHashCode(XYZ obj)
{
return Math.Round(obj.X, RoundingDigits, MidpointRounding.AwayFromZero).GetHashCode() ^
Math.Round(obj.Y, RoundingDigits, MidpointRounding.AwayFromZero).GetHashCode();
}
}
//Use:
myList.Distinct(new RoundedXyzComparer(5));
Im trying to find out a way to convert a 2d array of one type to another in a single line of code.
This is a personal learning experience rather than a need to do it in one line!!
Ive gotten so far as to convert it into IEnumerable<Tuple<ushort,ushort>>. Not sure where to go from here.
int[,] X = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
var Result = (from e in X.OfType<int>() select e)
.Select(S => (ushort)S)
.Select((value, index) => new { Index = index, Value = value })
.GroupBy(x => x.Index / 2)
.Select(g => new ushort[,] { { g.ElementAt(0).Value,
g.ElementAt(1).Value } });
Need to somehow convert the collection of Tuples into a ushort[,]
EDIT:
Just clarifying the question.
How do I convert a int 2d array into a ushort 2d array using a single line of code in linq?
EDIT:
Ive updated my code.
I now have it resulting in a IEnumerable collection of ushort[,].
I need to now find a way to concatonate all these into a single ushort[,]
The best I could come up with to keep the result two dimensional is this:
var input = new [,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
var output = new ushort[input.GetUpperBound(0) + 1, input.GetUpperBound(1) + 1];
Buffer.BlockCopy(input.Cast<int>().Select(x => (ushort)x).ToArray(), 0, output, 0, input.GetLength(0) * input.GetLength(1) * sizeof(ushort));
Using explicit cast to ushort we could do this, I'm leaving it to you to explore consequences in the conversion and addressing them.
int[,] X = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
ushort[,] shortArray = new ushort[X.GetUpperBound(0)+1, X.GetUpperBound(1)+1];
for (int i = 0; i <= X.GetUpperBound(0); ++i)
{
for(int j=0;j<= X.GetUpperBound(1);j++)
shortArray[i, j] = (ushort)X[i,j];
}
In case you are interested on Jagged array instead of multidimensional array, use this.
var jagged = X.Cast<int>()
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / (X.GetUpperBound(1) +1))
.Select(x => x.Select(s=> (ushort)s.Value).ToArray())
.ToArray();
Working example
How about:
var Result = X.OfType<int>().Select(s => new { Index = (s + 1) / 2, Value = s})
.GroupBy(g => g.Index)
.Select(s => s.Select(g => (ushort)g.Value).ToArray())
.ToArray();
Assume that we have an array:
int[] values = new int[10];
values[0] = 1;
values[1] = 2;
values[2] = 3;
values[3] = 4;
values[4] = 6;
values[5] = 8;
values[6] = 2;
values[7] = 1;
values[8] = 3;
values[9] = 9;
And I have another array, say, the def array that defines the buckets:
int[] def= new int[3]; // defs holds the definition of the buckets
def[0] = 0;
def[1] = 5;
def2] = 10;
I want to use this def array to group the values array, in order to get the frequency distribution, using c#:
i.e.
bin[0] = 7; // the number of array values that lies between 0 and 5
bin[1] = 3; // the number of array values that lies between 5 and 10
I already found a solution with loops, but I am sure there is more elegant and neater way to do this operation; the linq / group by method.
How can I code this procedure using LINQ Group By?
Thanks in advance for anyone contributing to the answer,
Aykut
If I've understood you correctly, then you're looking something like this:
var array = new[] { 1, 2, 3, 4, 6, 8, 2, 1, 3, 9 };
var buckets = new[] { 0, 5, 10 };
var distributionFreq = buckets
.Skip(1) // we don't need the first bucket
.OrderBy(bucket => bucket) // just ensure, that buckets are ordered properly
.Select((bucket, i) => new
{
Min = buckets[i], // minimal value of range
Max = bucket // maximal value of range
})
.Select(range => new
{
Range = range,
NumberOfValuesAtRange = array.Count(item => item > range.Min && item < range.Max)
})
.ToArray();
First, you have to define the range of values (0..5, 5..10, and so on).
Second, count the number of values in source array, which fits the range.
Note, that you should define more precisely the criteria for the outermost values, e.g. does the value of 5 fits the first range, or the second one?
Try this:
var bin =
array
.GroupBy(x => x / 5)
.Select(x => x.Count())
.ToArray();
Or better yet this:
var lookup = array.ToLookup(x => x / 5);
var bin =
Enumerable
.Range(0, lookup.Max(x => x.Key) + 1)
.Select(x => lookup[x].Count())
.ToArray();
This second example works if there are some outlying numbers in the original array.
Or even better, using buckets:
var buckets = new [] { 0, 5, 10, };
var lookup = array.ToLookup(x => buckets.Where(b => x >= b).Count() - 1);
var bin =
Enumerable
.Range(0, lookup.Max(x => x.Key) + 1)
.Select(x => lookup[x].Count())
.ToArray();
I want to group a pointcloud based on 2 conditions
simple on Y so I wrote pointcloudH.GroupBy(KVP => KVP.Value.Y) where KVP is an KeyValuePair<string,System.Drawing.Point>
and now I want to group it also by X if X == (previousX + 1)
as far as I know I should us ThenBy() but what do I have to write between the brackets?
and here an example for a better illustration what I want to achieve
Sample pointcloud
(x|y) (1|1),(2|1),(4|1),(1|2),(2|3),(3|3),(4|3),(5|8),(9|10)
after step 1. it looks like this
group1 (1|1),(2|1),(4|1)
group2 (1|2)
group3 (2|3),(3|3),(4|3)
group4 (5|8)
group5 (9|10)
after step 2. it should look like this
group1 (1|1),(2|1)
group2 (4|1)
group3 (1|2)
group4 (2|3),(3|3),(4|3)
group5 (5|8)
group6 (9|10)
current code
var Hgroup = pointcloudH.OrderBy(KVP => KVP.Value.Y) // order by Y
.GroupBy(KVP => KVP.Value.Y) // groub by Y
.ThenBy(KVP => KVP.Value.X); // group by X ???
I don't think LINQ is the best tool for this kind of job, but it can be achieved. The important part is to think of the relation between your Point.X and the index of the relative Point in the Point.Y group. Once you realize you want to group them by Point.X - Index, you can do:
var Hgroup = pointcloudH.OrderBy(p => p.Y)
.GroupBy(p => p.Y)
.SelectMany(yGrp =>
yGrp.Select((p, i) => new {RelativeIndex = p.X - i, Point = p})
.GroupBy(ip => ip.RelativeIndex, ip => ip.Point)
.Select(ipGrp => ipGrp.ToList()))
.ToList();
Note that this will probably perform worst than a regular iterative algorithm. My pointcloudH is an array, but you can just change the lambda to reflect your own list. Also, remove the ToList() if you want to defer execution. This was to ease the result inspection in the debugger.
If you want to group all points in a Point.Y group regardless of their index (ie order by Point.X as well. Add ThenBy(p => p.X) after the first OrderBy clause.
Your problem cannot be solved by doing 2 separate group by clauses. I have created a little sample which should work for your problem. These are the key things that are happening in the code:
Construct 'mirror' array and insert a copy of the first item at index 0, this is used to keep track of the previous point
Create a variable that is incremented whenever a 'chain' is broken. This is whenever the next value is not equal to the previous + 1. This way we can group by an unique key per 'chain'.
class Program
{
public struct Point
{
public static Point Create(int x, int y)
{
return new Point() { X = x, Y = y };
}
public int X { get; set; }
public int Y { get; set; }
public override string ToString()
{
return string.Format("({0}|{1})", X, Y);
}
}
static void Main(string[] args)
{
//helper to avoid to much keystrokes :)
var f = new Func<int, int, Point>(Point.Create);
//compose the point array
//(1|1),(2|1),(4|1),(1|2),(2|3),(3|3),(4|3),(5|8),(9|10)
var points = new[] { f(1, 1), f(2, 1), f(4, 1), f(1, 2), f(2, 3), f(3, 3), f(4, 3), f(5, 8), f(9, 10) }.OrderBy(p => p.Y).ThenBy(p => p.X);;
//create a 'previous point' array which is a copy of the source array with a item inserted at index 0
var firstPoint = points.FirstOrDefault();
var prevPoints = new[] { f(firstPoint.X - 1, firstPoint.Y) }.Union(points);
//keep track of a counter which will be the second group by key. The counter is raised whenever the previous X was not equal
//to the current - 1
int counter = 0;
//the actual group by query
var query = from point in points.Select((x, ix) => new { current = x, prev = prevPoints.ElementAt(ix) })
group point by new { point.current.Y, prev = (point.prev.X == point.current.X - 1 ? counter : ++counter) };
//method chaining equivalent
query = points.Select((x, ix) => new { current = x, prev = prevPoints.ElementAt(ix) })
.GroupBy(point => new { point.current.Y, prev = (point.prev.X == point.current.X - 1 ? counter : ++counter) });
//print results
foreach (var item in query)
Console.WriteLine(string.Join(", ", item.Select(x=> x.current)));
Console.Read();
}
}