Get the inner points which are on a straight line - c#

The question I'm about to ask might seem a Geometry question at the first glance, but it actually can be solved by using LINQ, at least I hope so!
I have 5 points on a straight line two of them are at the ends of the line. How can I select the ones that are in the interior of the line (not at the end points) using LINQ?
public class Point
{
public double X;
public double Y;
}
var listOfPointsOnALine = new List<Point>
{
new Point { X = 2500, Y = 50 },
new Point { X = 2540, Y = 112.5 },
new Point { X = 2580, Y = 175 },
new Point { X = 2620, Y = 237.5 },
new Point { X = 2660, Y = 300 },
}
So using some LINQ on the list above should give me the list below:
innerPointsOnALine: {(2540, 112.5), (2580, 175), (2620, 237.5)}
The points are ordered in the original list
The points should be in the same order they appear in the original list (listOfPointsOnALine)

If I understand it correctly then I think you are looking for:
var newList = listOfPointsOnALine
.Skip(1)
.Take(listOfPointsOnALine.Count - 2)
.ToList();
You may have to check for length of list before doing this.

Related

Two Dimensional Array farthest point search

I am trying to retrieve the farthest point in a collection, by comparing the distances from each point in the pointsToSearchFrom List to each other point in the pointCloudToSearchList. You can see a sample scenario from the attached image. I am still not an expert in navigating these data structures, and this algorithm exceeds my current knowledge of traversing two dimensional arrays.
Here is the code that I have until know. Any help would be great.
public static void Test(List<Point3d> pointsToSearchFrom, List<Point3d> pointCloudToSearch)
{
int rows = pointsToSearchFrom.Count;
int columns = pointCloudToSearch.Count;
double[,] arrayDistance = new double [rows, columns];
for (int i = 0; i < pointsToSearchFrom.Count; i++)
{
for (int j = 0; j < pointCloudToSearch.Count; j++)
{
arrayDistance[i, j] = (pointsToSearchFrom[i] - pointCloudToSearch[j]).magnitude;
}
}
}
You can use a two-d array for this, but you do not have to.
What you need is the MaxBy method which can be found here:
https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/MaxBy.cs
We begin by constructing a sequence of pairs:
var pairs = from first in pointsToSearchFrom
from second in pointCloudToSearch
select new { first, second };
Note that in C# 7 you would use a tuple, not an anonymous type here.
Now we have a sequence of all possible pairs. Use MaxBy to find the pair that maximizes the given quantity:
var maxPair = pairs.MaxBy(pair => (pair.first - pair.second).magnitude);
And now you're done:
Console.WriteLine($"{maxPair.first} --> {maxPair.second});
If you have a large collection of points -- like, thousands or millions -- then there are special techniques you have to use, because if there are thousands of points then there will be millions of pairs, and if there are millions of points there will be trillions of pairs. But if you just have a small handful, this technique works fine.
I didn't catch on at first that this was 3D. I used a class and list to store the 2 points and the distances between them. That way I could not only get the longest distance but the 2 points producing the distance.
public class Distances
{
public double Distance { get; set; }
public Point3D FromPoint { get; set; }
public Point3D ToPoint { get; set; }
public Distances(Point3D from, Point3D to)
{
FromPoint = from;
ToPoint = to;
Distance = (to - from).Length;
}
}
private void OP2()
{
List<Point3D> searchFrom = new List<Point3D>()
{
new Point3D(20,30,50),
new Point3D(10,50,10),
new Point3D(30,40,20),
new Point3D(60,30,10)
};
List<Point3D> searchCloud = new List<Point3D>()
{
new Point3D(60,70,80),
new Point3D(110,30,30),
new Point3D(80,110,55),
new Point3D(90,20,90)
};
List<Distances> resultDistances = new List<Distances>();
foreach (Point3D p1 in searchFrom)
{
foreach (Point3D p2 in searchCloud)
{
Distances d = new Distances(p1, p2);
resultDistances.Add(d);
}
}
//The following is just for testing purposes, skip to var longestDistance
List<Distances> distancesInOrder = resultDistances.OrderByDescending(i => i.Distance).ToList<Distances>();
foreach (Distances d in distancesInOrder)
{
Debug.Print($"From Point ({d.FromPoint.X}, {d.FromPoint.Y}, {d.FromPoint.Z}) To Point ({d.ToPoint.X}, {d.ToPoint.Y}, {d.ToPoint.Z}) Distance = {d.Distance}");
}
var longestDistance = resultDistances.OrderByDescending(i => i.Distance).FirstOrDefault();
//this gives you the longest distance and the two points
MessageBox.Show($"First Point ({longestDistance.FromPoint.X}, {longestDistance.FromPoint.Y}, {longestDistance.FromPoint.Z}) Cloud Point ({longestDistance.ToPoint.X}, {longestDistance.ToPoint.Y}, {longestDistance.FromPoint.Z}) Distance = {longestDistance.Distance}");
}

Two lists of coords returning most distinct minimal for one list based on distance function

I have a list of coords using Tuple, since I don't have access to the Drawing library to use 'Point'.
List<Tuple<int,int>> coords = new List<Tuple<int,int>>();
string[] movement = new string[temp.Count];
for(int i=1000; i<=8000; i=i+2300)
for(int j=1000; j<=15000; j=j+2000)
coords.Add(Tuple.Create(j,i));
coords.RemoveAll(x=> 3500>= getDist(0,0,x.Item1,x.Item2) );
coords.RemoveAll(x=> 3500>= getDist(16000,9000,x.Item1,x.Item2) );
I have player pieces in a list, two examples below.
List<int[]> player = new List<int[]>() {new int[]{0,726,1084,0,0,5},new int[]{2,1481,2208,0,0,-1} };
//piece numb, loc_X, loc_Y, teamID, state, value
Turn-based movements must be figured, and when the state of a piece indicates scouting, I want to find the most minimal coord-point-set to each scouting piece, without two pieces going to the same set of coords. If I try to use a foreach loop like:
foreach(var myBust in temp) {
int minDist = coords.Select(x => getDist( x.Item1,x.Item2,myBust[1],myBust[2]) ).OrderBy(x => x).Distinct().First();
coords.RemoveAll(x => getDist(16000,9000,x.Item1,x.Item2) == minDist); }
then I fall into an issue that the first piece configured may not be the closest to a point-set as compared to another scouting piece, which is why the foreach loop doesn't work or me. Therefore I want some type of linq/lambda statement that can return the said "Distinct" minimal coord-point in the coord list in comparison (by getDist and Min) to all the player pieces.
//Not sure why this doesn't give me what I am looking for
int minDist= coords.Zip(player, (x,y) => getDist(x.Item1,x.Item2,y[1],y[2])).Min();
My dist method.
static int getDist(int x1, int y1, int x2, int y2)
{ return Convert.ToInt32( Math.Sqrt(Math.Pow(x1-x2,2) + Math.Pow(y1-y2,2) );}
So for an answer I am looking for a way to run a function on two lists, which runs a function (but doesn't aggregate anything together), and can return whatever I want from one or both of those lists.
Based on the answer from #Jacob I came up with the below so far:
string[] movement = new string[player.Count];
List<int[]> temp = player;
while(temp.Any()){
HashSet<int> dists = new HashSet<int>();
foreach(var myBust in temp)
{ dists.UnionWith(coords.Select(x => getDist(x.Item1,x.Item2,myBust[1],myBust[2]) )); }
foreach(var myBust in player)
{ if(coords.Exists(x => getDist( x.Item1,x.Item2,myBust[1],myBust[2]) == dists.Min() ) )
{
Tuple<int,int> result = coords.FindAll(x => getDist( x.Item1,x.Item2,myBust[1],myBust[2]) == dists.Min() ).First();
movement[player.IndexOf(myBust)] = "Move " + result.Item1 + " " + result.Item2;
Console.WriteLine("Player Number "+myBust[0]+" going a dist of "+dists.Min()+" to coords "+result.Item1+","+result.Item2);
coords.Remove(result);
temp.Remove(myBust);
}
}
}
This gives the correct output of "Player Number 2 going a dist of 1871 to coords 3000,3300".
That is at least the first iteration through but then throws the error "Collection was modified; enumeration operation may not execute."
Any suggestions or modifications would be appreciated.
As I understand it, you want each player piece to get its closest coordinates from a set of coordinates, and you also want each player piece to not share a coordinate. There is probably not a simple one-liner for that without avoiding terrible algorithmic complexity. You have two major concerns; finding the closest coordinates and eliminating previously-used coordinates or players.
So let's use this algorithm:
Create a list of player/coordinate pairs, ordered by distance.
Create an empty set of players & coordinates that have already been used.
Loop through the player/coordinate pairs. If the pair is for a player and coordinate that hasn't been assigned, use that point, since it should be the shortest, then mark the player & coordinate as assigned.
As stated by others, this code would be clearer if you used more descriptive objects instead of arrays, but let's stick with what we have.
Here's an attempt at this:
var players = new List<int[]>() { new int[] { 0, 726, 1084, 0, 0, 5 }, new int[] { 2, 1481, 2208, 0, 0, -1 } };
List<int[]> coords = new List<int[]>();
for (int i = 1000; i <= 8000; i = i + 2300)
for (int j = 1000; j <= 15000; j = j + 2000)
coords.Add(new int[2] { j, i });
coords.RemoveAll(x => 3500 >= getDist(0, 0, x[0], x[1]));
coords.RemoveAll(x => 3500 >= getDist(16000, 9000, x[0], x[1]));
var closestPoints =
from playerIdx in Enumerable.Range(0, players.Count())
let player = players[playerIdx]
from coordIdx in Enumerable.Range(0, coords.Count())
let theseCoords = coords[coordIdx]
orderby getDist(theseCoords[0], theseCoords[1], player[1], player[2])
select new { playerIdx, coordIdx };
var playerCoords = new Dictionary<int, int>();
var usedPlayers = new HashSet<int>();
var usedCoords = new HashSet<int>();
foreach (var pairing in closestPoints)
{
if (!usedPlayers.Contains(pairing.playerIdx)
&& !usedCoords.Contains(pairing.coordIdx))
{
playerCoords[pairing.playerIdx] = pairing.coordIdx;
usedPlayers.Add(pairing.playerIdx);
usedCoords.Add(pairing.coordIdx);
}
}
// Result is in playerCoords as { 0: 0, 1: 6 }
If you have a large number of players or coordinates, this still isn't terribly efficient, since you're computing the distance for every single player/coordinate pairing. You may want to find a shortcut to avoid computing some of these. See Closest Pair: A Divide-and-Conquer approach for ideas there.

How to add an item to a List<List<T>>?

I think this is the right question / way to do this:
I have a list of coords (x,y) and an object (Location) which has X and Y; I, then, have a function where I check for each adjacent location and I compare them with my List items (which I converted to a jagged array)
static List<Location> GetWalkableAdjacentSquares(int x, int y, PointWithID [][]Map, Location target)
{
var proposedLocations = new List<Location>()
{
new Location { X = x, Y = y - 1 },
new Location { X = x, Y = y + 1 },
new Location { X = x - 1, Y = y },
new Location { X = x + 1, Y = y },
};
return proposedLocations.Where(l => Map[l.X][l.Y].value == 0).ToList();
}
(0 means it is open path)
Now, my List, is a simple List, and, therefore, I have an error:
public static List<PointWithID> map = new List<PointWithID>();
so I need to to the List this way (I guess):
public static List<List<PointWithID>> map = new List<List<PointWithID>>();
my question is:
How do I add the items to the List that way?
Of course regular way does not work:
private void addItemsToMap()
{
try
{
foreach (PointWithID open in NewMapToShow.openPath)
{
map.Add(open);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
Any help, please?
Well, given you have a List<List<PointWithID>> which is a list of lists, when you call the Add() method you should add the generic type which the list expects. So, a new list should be added in the Add method. On the other hand, the inner list should add objects of PointWithID.
I am not sure what you are trying to do, but see the code bellow:
private void addItemsToMap()
{
foreach (PointWithID open in NewMapToShow.openPath)
map.Add(new List<PointWithID> { open }); // create a new list for each point
}
Or
private void addItemsToMap()
{
// add all points in the main list as a new item (a list)
map.Add(new List<PointWithID>(NewMapToShow.openPath));
}

how to add value in 'y' on same 'x' point in c# chart

I am trying to create a column chart based on a string array. if more than 1 strings correspond to same string value it should just add up the number and use same bar for representation of similar strings. However this code of mine results in representation on different bars (each having 1 as their value):
private void plot_chart(string[] DTCs)
{
foreach (string str in DTCs)
{
bool doNotaddSeries = false;
foreach (var ser in chart3.Series)
{
if (str == ser.Name) //series already exists
{
doNotaddSeries = true;
ser.Points.AddY(1);
//MessageBox.Show(str + " exists");
break;
}
}
if (!doNotaddSeries)
{
chart3.Series.Add(str);
chart3.Series[str].Points.AddY(1);
}
doNotaddSeries = false;
}
}
what I want is (lets say) if i have:
str[0]="abc"
str[1]="def"
str[2]="abc"
i want "abc" to be represented on single bar with a value of 2 on y. whereas "def" should have 1 value on y axis. What I am getting is "abc" being represented as 2 different bars but similar representation of color in legend
Just use linq GroupBy to process it for you then add them after
var DTCs = new [] {"abc", "def", "abc"};
var points = DTCs.GroupBy(str => str).Select(group => new
{
x = group.Key,
y = group.Count()
});
// loop through points adding them to the chart
foreach (var p in points)
{
// Assume this is correct for your chart object
chart3.Series.Add(p.x);
chart3.Series[p.x].Points.AddY(p.y);
}

Creating a two-dimensional array

I am trying to create a two dimensional array and I am getting so confused. I was told by a coworker that I need to create a dictionary within a dictionary for the array list but he couldn't stick around to help me.
I have been able to create the first array that lists the the programs like this
+ project 1
+ project 2
+ project 3
+ project 4
The code that accomplishes this task is below-
var PGList = from x in db.month_mapping
where x.PG_SUB_PROGRAM == SP
select x;
//select x.PG.Distinct().ToArray();
var PGRow = PGList.Select(x => new { x.PG }).Distinct().ToArray();
So that takes care of my vertical array and now I need to add my horizontal array so that I can see the total amount spent in each accounting period. So the final output would look like this but without the dashes of course.
+ program 1-------100---200---300---400---500---600---700---800---900---1000---1100---1200
+ program 2-------100---200---300---400---500---600---700---800---900---1000---1100---1200
+ program 3-------100---200---300---400---500---600---700---800---900---1000---1100---1200
+ program 4-------100---200---300---400---500---600---700---800---900---1000---1100---1200
I have tried to use a foreach to cycle through the accounting periods but it doesn't work. I think I might be on the right track and I was hoping SO could provide some guidance or at the very least a tutorial for me to follow. I have posted the code that I written so far on the second array below. I am using C# and MVC 3. You might notice that their is no dictionary within a dictionary. If my coworker is correct how would I do something like that, I took a look at this question using dictionary as a key in other dictionary but I don't understand how I would use it in this situation.
Dictionary<string, double[]> MonthRow = new Dictionary<string, double[]>();
double[] PGContent = new double[12];
string lastPG = null;
foreach (var item in PGRow)
{
if (lastPG != item.PG)
{
PGContent = new double[12];
}
var MonthList = from x in db.Month_Web
where x.PG == PG
group x by new { x.ACCOUNTING_PERIOD, x.PG, x.Amount } into pggroup
select new { accounting_period = pggroup.Key.ACCOUNTING_PERIOD, amount = pggroup.Sum(x => x.Amount) };
foreach (var P in MonthList)
{
int accounting_period = int.Parse(P.accounting_period) - 1;
PAContent[accounting_period] = (double)P.amount;
MonthRow[item.PG] = PGContent;
lastPG = item.PG;
}
I hope I have clearly explained my issue, please feel free to ask for any clarification needed as I need to solve this problem and will be checking back often. Thanks for your help!
hope this helps.
// sample data
var data = new Dictionary<string, List<int>>();
data.Add("program-1", new List<int>() { 100, 110, 130 });
data.Add("program-2", new List<int>() { 200, 210, 230 });
data.Add("brogram-3", new List<int>() { 300, 310, 330 });
// query data
var newData = (from x in data
where x.Key.Contains("pro")
select x).ToDictionary(v => v.Key, v=>v.Value);
// display selected data
foreach (var kv in newData)
{
Console.Write(kv.Key);
foreach (var val in kv.Value)
{
Console.Write(" ");
Console.Write(val.ToString());
}
Console.WriteLine();
}
output is:
program-1 100 110 130
program-2 200 210 230
Don't try to use anonymous types or LINQ projection to create new data types, especially if you're a beginner, you will just get confused. If you want a specialized data type, define one; e.g.:
public class Account
{
public string Name { get; private set; }
public decimal[] MonthAmount { get; private set; }
readonly int maxMonths = 12;
public Account(string name, ICollection<decimal> monthAmounts)
{
if (name == null)
throw new ArgumentNullException("name");
if (monthAmounts == null)
throw new ArgumentNullException("monthAmounts");
if (monthAmounts.Count > maxMonths)
throw new ArgumentOutOfRangeException(string.Format(" monthAmounts must be <= {0}", maxMonths));
this.Name = name;
this.MonthAmount = new decimal[maxMonths];
int i = 0;
foreach (decimal d in monthAmounts)
{
this.MonthAmount[i] = d;
i++;
}
}
}
Use instances of this type directly, you do not have to convert them to arrays, dictionaries, lists, or anything else:
var accountPeriods = new List<Account>();
accountPeriods.Add(new Account("program-1", new decimal[] { 1, 2, 3, 4 }));
You can use LINQ or whatever to query or alter instances of your new type:
foreach (Account a in accountPeriods)
foreach (decimal d in a.MonthAmount)
DoSomethingWith(d);
That should be enough to get you started.
I want to thank #Ray Cheng and #Dour High Arch for their help but I have figured out another way to accomplish this task and I wanted to post my code so that the next person that is having the same trouble can figure out their problem faster.
Above I split my code into more managable sections to explain my problem as clearly as I could and the code below has all those parts combined so you can see the big picture. This code returns an array that contains the program and the amounts for every month.
public virtual ActionResult getAjaxPGs(string SP = null)
{
if (SP != null)
{
var PGList = from x in db.month_mapping
where x.PG_SUB_PROGRAM == SP
select x;
var PGRow = PGList.Select(x => new { x.PG }).Distinct().ToArray();
float[] PGContent = new float[12];
Dictionary<string,float[]> MonthRow = new Dictionary<string, float[]>();
foreach (var item in PGRow)
{
PGContent = new float[12];
var MonthList = from x in db.month_Web
where x.PG == item.PG
group x by new { x.ACCOUNTING_PERIOD, x.PG, x.Amount } into pggroup
select new { accounting_period = pggroup.Key.ACCOUNTING_PERIOD, amount = pggroup.Sum(x => x.Amount) };
foreach (var mon in MonthList)
{
int accounting_period = int.Parse(mon.accounting_period) - 1;
PGContent[accounting_period] = (float)mon.amount/1000000;
}
MonthRow[item.PG] = PGContent;
}
return Json(MonthRow, JsonRequestBehavior.AllowGet);
}
return View();
}
This code worked great for me since I am pulling from a Linq to SQL query instead of adding data directly into the code. My problems stemmed from mainly putting the data pulls outside of the foreach loops so it only pulled 1 piece of data from the SQL instead of all twelve months. I hope this helps some one else who is trying to pull data in from SQL data sources into multidimensional arrays.

Categories