convert two loops over two dimensional array to Linq syntax - c#

I have a two dimensional array containing objects of type MyObj.
private MyObj[,] myObjs = new MyObj[maxX, maxY];
I want to get the indices from the array when passing in a matching object. I want to get the x and y value from this array. I can return these two values as a Position object that takes a x and y coordinate.
private Position GetIndices(MyObj obj)
{
for (int x = 0; x < myObjs.GetLength(0); x++)
{
for (int y = 0; y < myObjs.GetLength(1); y++)
{
if (myObjs[x, y] == obj)
{
return new Position(x, y);
}
}
}
}
Is it possible to get this code shorten to some Linq code lines?

But I don't think, it looks nice :)
var result = Enumerable.Range(0, myObjs.GetLength(0))
.Select(x => Enumerable.Range(0, myObjs.GetLength(1)).Select(y => new { x, y }))
.SelectMany(o => o)
.FirstOrDefault(o => myObjs[o.x, o.y] == obj);

Here's another option, if you're interested. It uses an indexer inside the first select and does a little math to find where that index falls inside the two-dimensional array.
var o = new MyObj();
myObjs[1,2] = o;
var p = myObjs.Cast<MyObj>()
.Select((x,i) => Tuple.Create(x,i))
.Where(x => x.Item1 == o)
.Select(x => new Point(x.Item2 / myObjs.GetLength(1), x.Item2 % myObjs.GetLength(1)))
.SingleOrDefault();
Console.WriteLine(p); // prints {X=1,Y=2}
It sorta looks like you're considering the x-coordinate to be the height of the array, and the y-coordinate the width, in which case you'd want to switch it up slightly:
var p = myObjs.Cast<MyObj>()
.Select((x,i) => Tuple.Create(x,i))
.Where(x => x.Item1 == o)
.Select(x => new Point(x.Item2 % myObjs.GetLength(1), x.Item2 / myObjs.GetLength(1)))
.SingleOrDefault();
Console.WriteLine(p); // prints {X=2,Y=1}
I used Point instead of Position since it's built into .NET but you should be able to just swap one for the other.

Yes, that is possible. You can ask Resharper to do the job (loop to linq) for you. After installation, just use the feature.

Related

Convert array of doubles to list object using linq

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) });
}

How to group in Linq based on previous Value

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();
}
}

Counting letter frequencies

I'm reading in a text file using StreamReader to the program. I need to record the frequency of each letter in the string into an array (where index 0 would be A, and so on). What's the simplest approach for this?
Edit: I had this originally, until I realized it was completely wrong.
int counter = 0;
int[] freq = new int[26]; // create frequency array
// counts frequency
while (counter < inValue.Length)
{
int A = 65; // ASCII value for "A"
char x = char.Parse(inValue.Substring(counter, 1)); // get individual characters from string
int s = (int)x; // cast character to integer value
if (s == A + counter)
freq[counter]++;
counter++;
}
Where inValue is the text file StreamReader reads into the program.
var freqs = File.ReadAllText("myfile.txt")
.Where(c => Char.IsLetter(c))
.GroupBy(c => c)
.ToDictionary(g => g.Key, g => g.Count());
This should give you a Dictionary of characters and their count.
Update:
If you want case insensitive counts, just change the GroupBy:
.GroupBy(c => Char.ToUpper(c)) // instead of .GroupBy(c => c)
And in my opinion a dictionary is better than an array in this case because the character that the "count" belongs to is not just implied by the index; instead, it is an explicit key. This makes lookups easier because you don't have to convert the character to an index. Additionally, this makes it more flexible when adding internationalization support. However, if you absolutely need an array, it is a simple change:
var freqs = File.ReadAllText("myfile.txt")
.Where(c => Char.IsLetter(c))
.GroupBy(c => c)
.OrderBy(g => g.Key)
.Select(g => g.Count())
.ToArray()
You can try something like this. This worked for me but I didnt used StreamReader:-
int[] c = new int[(int)char.MaxValue];
string s = File.ReadAllText("text.txt");
foreach (char t in s)
{
c[(int)t]++;
}
for (int i = 0; i < (int)char.MaxValue; i++)
{
if (c[i] > 0 &&
char.IsLetterOrDigit((char)i))
{
Console.WriteLine("Letter: {0} Frequency: {1}",(char)i, c[i]);
}
}
A few modifications to your code will make it work, assuming that you only want to count the letters 'A' through 'Z':
int counter = 0;
int[] freq = new int[26]; // create frequency array
// counts frequency
while (counter < inValue.Length)
{
char c = invalue[counter];
if (c >= 'A' && c <= 'Z')
{
++freq[(int)c - 65]
}
++counter;
}
If you want to count lower case letters as well, then change the first line in the loop to:
char c = char.ToUpper(invalue[counter]);
I spent quite a while to figure out this Linq which will result in the exact same array you want:
int[] occurance = File.ReadAllText("myfile.txt")
.Where(c => char.IsLetter(c))
.Select(c => (int)char.ToUpperInvariant(c) - 65)
.GroupBy(a => a)
.ToDictionary(a => a.Key, a => a.Count())
.OrderBy(a => a.Key)
.Select(a => a.Value)
.ToArray();

Get indexes for intersection

I have a problem. I'm finding an intersection using the code below:
Envelope[][] extents = new Envelope[tilesCountX][tilesCountY];
// some code here to set "extents" values
var intersectedTiles =
extents
.SelectMany(es => es)
.Where(e => EnvIntersects(e, bounds))
.ToList();
private static bool EnvIntersects(Envelope e1, Envelope e2)
{
return e1.MinX >= e2.MinX && e1.MaxX <= e2.MaxX && e1.MinY >= e2.MinY && e1.MaxY <= e2.MaxY;
}
It works but I want to get the indexes of intersected extents.
e.g.
If extents[2][7] is an intersected element, I want to get 2 and 7.
Is it possible by modifying my code?
[edit]
bounds is an Envelope that has MinX, MinY, MaxX and MaxY properties inside.
Envelope bounds = new Envelope();
bounds.MinX = some_value_1;
bounds.MaxX = some_value_2;
bounds.MinY = some_value_3;
bounds.MaxY = some_value_4;
I think this could also give you what you want:
var intersectedIndices =
from x in Enumerable.Range(0, tilesCountX)
from y in Enumerable.Range(0, tilesCountY)
where EnvIntersects(extents[x, y], bounds)
select new { x, y };
Try this:
var intersectedTiles =
extents
.SelectMany((es,i) => es.Select((x,j)=>new{I=i,J=j,Element = x}))
.Where(e => EnvIntersects(e.Element, bounds))
.ToList();
where for all elements in intersectedTiles this is true:
intersectedTiles[x].Element == extents[intersectedTiles[x].I][intersectedTiles[x].J]

Filtering two arrays to avoid Inf/NaN values

I have two arrays of doubles of the same size, containg X and Y values for some plots.
I need to create some kind of protection against INF/NaN values. I need to find all that pairs of values (X, Y) for which both, X and Y are not INF nor NaN
If I have one array, I can do it using lambdas:
var filteredValues = someValues.Where(d=> !(double.IsNaN(d) || double.IsInfinity(d))).ToList();
Now, for two arrays I use the following loop:
List<double> filteredX=new List<double>();
List<double> filteredY=new List<double>();
for(int i=0;i<XValues.Count;i++)
{
if(!double.IsNan(XValues[i]) &&
!double.IsInfinity(XValues[i]) &&
!double.IsNan(YValues[i]) &&
!double.IsInfinity(YValues[i]) )
{
filteredX.Add(XValues[i]);
filteredY.Add(YValues[i]);
}
}
Is there a way of filtering two arrays at the same time using LINQ/lambdas, as it was done for the single array?
Unfortunately I can use only .NET 3.5.
Slight correction for Mark's original answer:
var filteredValues = XValues.Zip(YValues, (x,y) => new { x, y })
.Where(p => !(double.IsNan(p.x) || double.IsNan(p.y) ||
double.IsInfinity(p.x) || double.IsInfinity(p.y)))
.ToList();
Alternatively, you might want to make it slightly neater:
Func<double, bool> valid = z => !double.IsNan(z) && !double.IsInfinity(z);
var filteredValues = XValues.Zip(YValues, (x,y) => new { x, y })
.Where(p => valid(p.x) && valid(p.y))
.ToList();
If you then need the results back into two lists, you can do:
var filteredX = filteredValues.Select(p => p.x).ToList();
var filteredY = filteredValues.Select(p => p.y).ToList();
C# 4.0 introduces the Enumerable.Zip extension method to accomplish iterating over enumerables "in parallel" as you describe.
I haven't used it myself, but it should be something like:
var filteredValues =
XValues.Zip(YValues, (x,y) => new { X = x, Y = y})
.Where( o =>
!(double.IsNan(o.X) || double.IsNan(o.Y) || double.IsInfinity(o.X) || double.IsInfinity(o.Y)))
.ToList();
(Sorry for the funny indentation, wanted it to be more readable on SO)
OK, so you can't use .NET 4.0 and therefore can't use the Zip extension.
Or can you?
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TSecond, TResult> resultSelector)
{
using (var eFirst = first.GetEnumerator())
using (var eSecond = second.GetEnumerator())
{
while (eFirst.MoveNext() && eSecond.MoveNext())
yield return resultSelector(eFirst.Current, eSecond.Current);
}
}
See for yourself :)
static void Main(string[] args)
{
var x = new double[] { 0.0, 1.0, 2.0, double.NaN, 4.0, 5.0 };
var y = new double[] { 0.5, 1.5, double.PositiveInfinity, 3.5, 4.5, 5.5 };
// note: using KeyValuePair<double, double> --
// you could just as easily use your own custom type
// (probably a simple struct)
var zipped = x.Zip(y, (a, b) => new KeyValuePair<double, double>(a, b))
.Where(kvp => IsValid(kvp.Key) && IsValid(kvp.Value))
.ToList();
foreach (var z in zipped)
Console.WriteLine("X: {0}, Y: {1}", z.Key, z.Value);
}
static bool IsValid(double value)
{
return !double.IsNaN(value) && !double.IsInfinity(value);
}
Output:
X: 0, Y: 0.5
X: 1, Y: 1.5
X: 4, Y: 4.5
X: 5, Y: 5.5
You can try to do this:
doubleArray1.Zip(doubleArray2, (x, y) => Tuple.Create(x, y))
.Where(tup => !double.IsNaN(tup.Item1) &&
!double.IsNaN(tup.Item2) &&
!double.IsInfinity(tup.Item1) &&
!double.IsInfinity(tup.Item1));
Alternately, you could make a method for filtering and zipping at the same time, the major benefit is that you're not limited to C# 4:
public static IEnumerable<Tuple<TOne, TTwo>> DualWhere<TOne, TTwo>(this IEnumerable<TOne> one, IEnumerable<TTwo> two, Func<TOne, TTwo, bool> predicate)
{
var oneEnumerator = one.GetEnumerator();
var twoEnumerator = two.GetEnumerator();
while (oneEnumerator.MoveNext() && twoEnumerator.MoveNext())
{
if (predicate(oneEnumerator.Current, twoEnumerator.Current))
yield return Tuple.Create(oneEnumerator.Current, twoEnumerator.Current);
}
oneEnumerator.Dispose();
twoEnumerator.Dispose();
}
Edit: The latter should work with C# 3.5.
Here's a solution that will work in C# 3 and .NET 3.5
List<double> list1 = new List<double>() { 1.2, 3.8, double.NaN, 17.8 };
List<double> list2 = new List<double>() { 9.4, double.PositiveInfinity, 10.4, 26.2 };
var query = from x in list1.Select((item, idx) => new { item, idx })
where !double.IsNaN(x.item) && !double.IsInfinity(x.item)
join y in list2.Select((item, idx) => new { item, idx })
on x.idx equals y.idx
where !double.IsNaN(y.item) && !double.IsInfinity(y.item)
select new { X = x.item, Y = y.item };
Iterating over the query would produce pairs containing 1.2 & 9.4 and 17.8 & 26.2. The middle two pairs would be discarded because one contains NaN and the other contains infinity.
foreach (var pair in query)
{
Console.WriteLine("{0}\t{1}", pair.X, pair.Y);
}

Categories