I want to change a value in an array that holds a struct:
SitSpot[] _spots = new SitSpot[4];
The struct fields are:
struct SitSpot
{
public SitSpot(Transform spot, bool isOccupied, int id)
{
IsOccupied = isOccupied;
Spot = spot;
Id = id;
}
public bool IsOccupied;
public Transform Spot;
public int Id;
}
However, when accessing the array element and trying to modify it, the original array element remains unchanged
SitSpot spot = _spots.Where(x => x.IsOccupied != true).OrderBy(x => Vector3.Distance(x.Spot.position, driver.transform.position)).FirstOrDefault();
spot.IsOccupied = true;
So it is necessary to find the index of the array element by to modify specified value:
SitSpot spot = _spots
.Where(x => x.IsOccupied != true)
.OrderBy(x => Vector3.Distance(x.Spot.position, driver.transform.position))
.FirstOrDefault();
spot.IsOccupied = true;
int i = Array.FindIndex(_spots, x => x.Id == spot.Id);
_spots[i] = spot;
My question is, can I access the element inside the array and modify it directly rather than changing the value and reassigning it?
The best choice in your situation seems not to use LINQ. I propose following
static class Extensions
{
public static void SetOccupied(this SitSpot[] spots, Driver driver)
{
int index = -1;
int distance = int.MaxValue;
for (int i = 0; i < spots.Length; i++)
{
if (spots[i].IsOccupied != true)
{
var distance2 = Vector3.Distance(spots[i].Spot.position,
driver.transform.position);
if (distance >= distance2)
{
distance = distance2;
index = i;
}
}
}
if (index != -1)
{
spots[index].IsOccupied = true;
}
}
}
Related
I’ve written an implementation of A* that relies on sorting nodes by their F score in a sortedSet.
The sorting, in some cases, seems to insert a Node object at the 'Min' value when its compared 'F' value is actually the second lowest rather than the Min, as described. I'm completely baffled as to why this is happening. I believe it's causing the knock-on effect of causing nodeTree.Remove and nodeTree.RemoveWhere to fail, but that might be the actual cause of the issue, I'm honestly not sure - though I wouldn't know how to fix it if it is.
This is the comparer used. I assume it's relatively obvious that I'm not exactly sure how to implement these, but I think this should work as I intend.
public class FValueFirst : Comparer<PathfindingAgent.Node>
{
public override int Compare(PathfindingAgent.Node x, PathfindingAgent.Node y)
{
int result = x.F.CompareTo(y.F);
if (result == 0)
{
result = y.G.CompareTo(x.G);
}
if(x == y)
{
result = 0;
}
return result;
}
}
This is the Node object, for reference.
public class Node
{
public Cell cell;
public float G;
public float H;
public bool Opened;
public bool Closed;
public Node Previous;
public float F { get => G + H; }
}
This is the function it all occurs in. The result is deterministic, thankfully. Depending on the current destID and the particular layout of the grid's obstacles it will always get out of sort on the same iteration.
public void PathTo(Vector3Int destID)
{
SortedSet<Node> nodeTree = new SortedSet<Node>(new FValueFirst());
Vector3Int radius = PathfindingGrid.Instance.GridRadius;
NodeGrid = new Node[radius.x * 2 + 1, radius.y * 2 + 1, radius.z * 2 + 1];
Node startNode = new Node()
{
cell = PathfindingGrid.Cells[CurrentID.x, CurrentID.y, CurrentID.z],
G = 0,
H = 0
};
Node endNode = new Node()
{
cell = PathfindingGrid.Cells[destID.x, destID.y, destID.z],
G = 0,
H = 0
};
Vector3Int sID = startNode.cell.ID;
Vector3Int eID = endNode.cell.ID;
NodeGrid[sID.x, sID.y, sID.z] = startNode;
NodeGrid[eID.x, eID.y, eID.z] = endNode;
if (endNode.cell.IsOccupied) return;
nodeTree.Add(startNode);
int iterations = 0;
while(true)
{
Node node;
node = nodeTree.Min;
node.Closed = true;
nodeTree.RemoveWhere(n => n == node);
if(node == nodeTree.Min)
{
throw new Exception($"Incorrect node was removed from the tree");
}
if (node == endNode)
{
List<Node> chain = BacktraceChain(node);
Debug.Log($"Path found from {CurrentID} to {destID} with score {endNode.G} traversing {chain.Count} cells in {iterations} iterations");
DrawLine(chain, Color.white);
break;
}
List<Node> neighbours = GetNeighbours(node);
foreach(Node neighbour in neighbours)
{
if (neighbour == startNode || neighbour.Closed) continue;
float newg = Vector3Int.Distance(node.cell.ID, neighbour.cell.ID) + node.G;
if (!neighbour.Opened || newg < neighbour.G)
{
neighbour.G = newg;
neighbour.H = ManhattanHeuristic(neighbour, endNode);
neighbour.Previous = node;
if(!neighbour.Opened)
{
nodeTree.Add(neighbour);
neighbour.Opened = true;
}
else
{
nodeTree.RemoveWhere(n => n == neighbour);
nodeTree.Add(neighbour);
}
}
}
iterations++;
}
}
For posterity, I solved the issue - it was due to my inexperience with the SortedList type.
This code, found near the end of the function was to blame
if (!neighbour.Opened || newg < neighbour.G)
{
neighbour.G = newg;
neighbour.H = ManhattanHeuristic(neighbour, endNode);
neighbour.Previous = node;
if(!neighbour.Opened)
{
nodeTree.Add(neighbour);
neighbour.Opened = true;
}
else
{
nodeTree.RemoveWhere(n => n == neighbour);
nodeTree.Add(neighbour);
}
Specifically, an item in a tree cannot have its compared values modified to the point where it no longer compares correctly in that index. The item must first be removed from the list, modified, and readded.
My guess in hindsight is that, though removed immediately after modification, the tree is unable to be sufficiently traversed to access the target item due to the modification.
Thus my solution was to simply re-arrange the block so that the removal and addition occured on either side of the modification respectively, like so:
if (!neighbour.Opened || newg < neighbour.G)
{
if (neighbour.Opened)
{
if (!nodeTree.Remove(neighbour)) throw new Exception($"{neighbour} was not removed from tree");
}
else
{
neighbour.Opened = true;
}
neighbour.G = newg;
neighbour.H = ManhattanHeuristic(neighbour, endNode);
neighbour.Previous = node;
nodeTree.Add(neighbour);
}
Can this be simplified?
public int ReplaceNameInHistoryForPublisher(string OldName, string NewName)
{
int iSize = _DutyAssignments.Count;
int iTotalReplaced = 0, iTotal = 0;
try
{
for (int i = 0; i < iSize; i++)
{
DutyAssignmentEntry oEntry = _DutyAssignments[i];
int iSizeAssign = oEntry.Assignments.Count;
for(int iAssign = 0; iAssign < iSizeAssign; iAssign++)
{
if(oEntry.Assignments[iAssign].Name == OldName)
{
oEntry.Assignments[iAssign].Name = NewName;
iTotal++;
}
}
if(iTotal > 0)
{
_DutyAssignments[i] = oEntry;
iTotalReplaced += iTotal;
}
}
return iTotalReplaced;
}
catch (Exception ex)
{
SimpleLog.Log(ex);
return 0;
}
}
I have a List of DutyAssignmentEntry objects. Each of these objects has an Assignments property. As expected, that variable is a List of Assignment objects.
The Assignment object has a Name property which is what i am looking at to update.
My code works but I wondering if it can be improved with LINQ?
Yes, you can simplify it:
public int ReplaceNameInHistoryForPublisher(string OldName, string NewName)
{
var assignmentsToUpdate = _DutyAssignments
.SelectMany(da => da.Assignments.Where(a => a.Name == OldName))
.ToList();
assignmentsToUpdate.ForEach(x => x.Name = NewName);
return assignmentsToUpdate.Count;
}
But note that LINQ is not the right tool to update a collection but to query it. You can use it to find out what you have to update. I hide the loops in the LINQ query and in List<T>.ForEach.
Btw, Assignment is a reference type, so you can simply change the Name property, you don't need to overwrite this reference in the list with itself(_DutyAssignments[i] = oEntry).
I'm having a little trouble reading values in from a database and assigning them to an array. It seem to work in my unit tests, but in practice some values are missing.
Here's my database code:
private void GetParameterValuesFromDatabase()
{
this.parameterValues = (from DataRow r in this.database.RunCommand("select * from KST_PARAM_VALUES v join DM_PARM_NAME p on v.PARM_NAME_KEY = p.PARM_NAME_KEY").Rows
where (int)r["SCENARIO_KEY"] == this.scenario.ScenarioKey
select new DatabaseParameter
{
ParameterValuesKey = r.Field<int>(0),
ProfileType = r.Field<string>(1),
ScenarioKey = r.Field<int>(2),
StressEditorKey = r.Field<int>(3),
StressClassKey = r.Field<int>(4),
PeriodKey = r.Field<int>(5),
ParameterNameKey = r.Field<int>(6),
ParameterValue = r.Field<double>(7),
ActiveStress = (r.Field<string>(8) == "Y") ? true : false,
ParameterKey = (int)r["PARM_NUMBER"]
}).ToDictionary(r => r.ParameterValuesKey, r => r);
}
Not having any issues with this part of my code, just showing for completeness.
private void LoadParameters()
{
this.GetParameterValuesFromDatabase();
// TODO: Assuming 9 periods for now, change to allow for variable periods
for (int i = 1; i <= MaxNumberOfStressPeriods; i++)
{
this.parametersByPeriod.Add(i, this.parameterValues.Where(t => t.Value.PeriodKey == i).ToDictionary(t => t.Key, t => t.Value));
}
Log.Instance.LogMessage(LogLevel.Debug, "Created parameter dictionaries from database");
// For every stress editor in the dictionary of stress editors
foreach (KeyValuePair<int, ClassList> ed in this.stressParams)
{
// For every type of class selector
foreach (ClassSelector c in Enum.GetValues(typeof(ClassSelector)))
{
// For each of the classes within each class list within the editor
for (int i = 0; i < ed.Value.ClassLists[c].Count; i++)
{
string className = ed.Value.ClassLists[c][i].Name;
// For each double array in each class
foreach (KeyValuePair<int, double[]> t in ed.Value.ClassLists[c][i].ClassVariables.EditorParameters)
{
double[] values = this.GetParameterValues(t.Key, ed.Key, className);
BasicStressEditorVariables.AddParameters(values, ed.Value, className, t.Key);
}
}
}
}
}
}
Above shows the overall LoadParameters() method.
Below we have some code that selects 9 values from the dictionary constructed from the database, ready to be added to the array.
private double[] GetParameterValues(int paramKey, int editorKey, string className)
{
double[] values = new double[9];
for (int i = 1; i <= MaxNumberOfStressPeriods; i++)
{
Dictionary<int, DatabaseParameter> temp = this.parametersByPeriod[i];
foreach (KeyValuePair<int, DatabaseParameter> d in temp)
{
if (d.Value.ParameterKey == paramKey && d.Value.PeriodKey == i && d.Value.StressEditorKey == editorKey && d.Value.ProfileType == className)
{
values[i - 1] = d.Value.ParameterValue;
}
}
}
return values;
}
Below shows getting the destination array from the dictionary, as indexes cannot be passed by reference
public static void AddParameters(double[] values, ClassList editor, string className, int paramKey)
{
// TODO: Maybe search all lists to eliminate the need for the class selector as a parameter
// TODO: Will throw an exception when nothing is found. Handle it
ParameterClass p = null;
foreach (ClassSelector c in Enum.GetValues(typeof(ClassSelector)))
{
p = editor.ClassLists[c].FirstOrDefault(f => f.Name == className);
if (p != null)
{
break;
}
}
// TODO: Notify that could not be found
if (p == null)
{
Log.Instance.LogMessage(LogLevel.Error, $"Unable to find class {className}");
return;
}
double[] dest = p.ClassVariables.editorParameters[paramKey];
AddParameterValues(values, ref dest);
}
And here's the AddParameterValues() method:
private static void AddParameterValues(double[] values, ref double[] destination)
{
if (values.Length != destination.Length)
{
return;
}
for (int i = 0; i < values.Length; i++)
{
destination[i] = values[i];
}
}
Debugging shows that some values are being loaded into the destination array, but some aren't. Could anyone tell me why this is? Or if not, point me toward some material?
Thank you for your time
I'm not that C# specialist but looking to following code as a C programmer
private double[] GetParameterValues(int paramKey, int editorKey, string className)
{
double[] values = new double[9];
//...
return values;
}
I would assume that the lifetime of values is only within the function GetParameterValues and the function GetParameterValues delivers the caller with reference to a dead variable.
What if you change the prototype to something like
private void GetParameterValues(ref double[] values, int paramKey, int editorKey, string className)
I have a List<List<Object>> in which one of these objects has the property isCurrentlySelected = true. This nested list represents a grid of these objects that might be constructed in any configuration (i.e. the grid may be any dimensions, and may even be jagged).
Now, some of these objects have the property isSelectable = false, so I'm trying to create a method which takes a single parameter for "direction" and returns the first eligible index in the specified direction from the one currently marked as selected.
So far I've only been able to do this using several nested for loops and if-else statements, and even then, it will only work for one direction. I'm wondering if there's a more elegant way to check for the first eligible element, preferably using a single method.
Thanks in advance for any help,
~KWiP
EDIT: with code sample
So I have the Menu class:
public class Menu
{
public int x;
public int y;
public int width;
public int height;
public int lineHeight;
public float menuTimer = 0.0f;
public bool menuKeysPressed = false;
public List<List<Selection>> selections = new List<List<Selection>>();
public int selectedList = 0;
public int selectedItem = 0;
in which is the child class Selection:
public class Selection
{
public int x;
public int y;
public bool isCurrentlySelected = false;
public bool isSelectable = true;
public Color selectedColor = Color.White;
public Color unselectedColor = Color.Gray;
public bool isNonText = false;
public string displayText = "";
public int textSize;
public Selection(int xPos, int yPos, string text, int size, bool selectable)
{
x = xPos;
y = yPos;
displayText = text;
textSize = size;
if (!selectable)
{
isCurrentlySelected = false;
}
}
}
Each Selection in a Menu object has X and Y coordinates in the form of its indices in the nested lists found in Menu.selections. In each Menu, there is typically exactly one Selection with its isCurrentlySelected property set to true.
public Menu(int xPos, int yPos, int wdth, int hght, int lists = 0, int items = 0, bool allSelectable = true)
{
x = xPos;
y = yPos;
width = wdth;
height = hght;
if (items > 0 && lists <= 0)
{
lists = 1;
}
for (int i = 0; i < lists; i++)
{
selections.Add(new List<Selection>());
for (int j = 0; j < items; j++)
{
selections.ElementAt(i).Add(new Selection(((wdth / items) * j) + xPos, ((hght / lists) * i) + yPos, "", 20, allSelectable));
}
}
if (items > 0)
{
selections.ElementAt(0).ElementAt(0).isCurrentlySelected = true;
}
}
Now, within the Menu class, I'm trying to make a method which will deselect the currently selected index, and then select the next closest eligible index in a particular direction, wrapping around to the other side if the end of the range of indices is reached. Unfortunately, all I've been able to come up with is this mess, which currently only works going North, and would need to be expanded to roughly 4x its size to accommodate all directions.
public void nav(Point currentSelected, int dir) // The 4 cardinal directions are represented by an int: 0 for North and continuing clockwise from there.
{
int newRow = currentSelected.Y;
int newIndex = currentSelected.X;
switch (dir)
{
case 0: // Operations to select next eligible N index.
if (this.selections.Count <= 1)
{
return;
}
else
{
int firstOpenRow = this.selections.Count;
for (int i = 0; i < this.selections.Count; i++)
{
int difference = i - currentSelected.Y;
if (difference > 0 && difference < firstOpenRow && this.selections.ElementAt(i).ElementAt(currentSelected.X).isSelectable == true)
{
firstOpenRow = i;
}
}
if (firstOpenRow == this.selections.Count)
{
for (int i = 0; i < this.selections.Count; i++)
{
int difference = i - currentSelected.Y;
if (difference < 0 && difference < firstOpenRow && this.selections.ElementAt(i).ElementAt(currentSelected.X).isSelectable == true)
{
firstOpenRow = i;
}
}
if (firstOpenRow == this.selections.Count)
{
firstOpenRow = currentSelected.Y;
}
}
this.selections.ElementAt(currentSelected.Y).ElementAt(currentSelected.X).isCurrentlySelected = false;
this.selections.ElementAt(firstOpenRow).ElementAt(currentSelected.X).isCurrentlySelected = true;
}
break;
case 1:
// Add operations for E here
break;
case 2:
// Add operations for S here
break;
case 3:
// Add operations for W here
break;
}
}
Your choice of data-structure isn't really well made for the kind of queries you need to make. Maybe you should consider investing a little more time into creating a linked grid. Something like:
class Item
{
Item North { get; private set; }
Item South { get; private set; }
Item West { get; private set; }
Item East { get; private set; }
}
Like that, if you look for the next 'left' item, you can just go West until you either reach null or you encounter an item that is selectable.
Otherwise, it makes sense to distinguish between the different kinds of directions. Just because you have less code doesn't mean your program runs faster. Think of your algorithm in terms of how many operations you have to make to reach your goal, instead of how long it is.
Sorry if that's not the answer your're looking for.
I have a collection that I am inserting into the Dictionary and there are values that have already been entered into the KVPs. Say I already have a key of "4000" and the value comes up again, what happens to the value? Does it add the value of the key instance to the value that already exists for that key? Does it over write the value?
If it over writes, how can I add the values as they iterate through the collection of values?
public class AccountBalance
{
public decimal balance { get; set; }
public bool balancesheetact { get; set; }
public AccountBalance()
{
balance = 0;
balancesheetact = false;
}
}
Dictionary<string, List<AccountBalance>> balances = new Dictionary<string, List<AccountBalance>>();
var jentries = from je in gl.JEHeaders
where je.ped_int_id >= beginperiod && je.ped_int_id <= endperiod
orderby je.ped_int_id
select je;
bool isBalanceSheet;
foreach (JEHeader header in jentries)
{
foreach (JEDetail entry in header.JEDetails)
{
string subAccount = entry.ChartOfAccounts.acc_ext_id.Trim();
string key = subAccount.Remove(0, 4);
if (entry.ChartOfAccounts.acc_ty >= 15320 && entry.ChartOfAccounts.acc_ty <= 15322)
isBalanceSheet = true;
else
isBalanceSheet = false;
AccountBalance value = null;
if (!balances.ContainsKey(key))
{
List<AccountBalance> account_balances = new List<AccountBalance>();
// for (int i = 0; i < 12; ++i)
for (int i = 0; i < 14; ++i)
account_balances.Add(new AccountBalance());
balances.Add(key, account_balances);
}
// value = balances[key][header.ped_int_id % beginperiod];
value = balances[key][(header.ped_int_id % beginperiod) + 1];
/// NEW
if (header.ped_int_id == 637)
value = balances[key][0];
/// end NEW
if (entry.jnl_pst_deb_at != null)
value.balance += entry.jnl_pst_deb_at.HasValue ? entry.jnl_pst_deb_at.Value : 0;
if (entry.jnl_pst_crd_at != null)
value.balance -= entry.jnl_pst_crd_at.HasValue ? entry.jnl_pst_crd_at.Value : 0;
if (isBalanceSheet == true)
value.balancesheetact = true;
else
value.balancesheetact = false;
}
}
balances = balances.OrderBy(kvp => kvp.Key).ToDictionary(xyz => xyz.Key, xyz => xyz.Value);
int row = 0;
decimal ytdbalance;
foreach (KeyValuePair<string, List<AccountBalance>> account in balances)
{
row++;
//string subAccount = account.Key.Remove(0, 4);
workbook.AddCell(row, 0, account.Key);
ytdbalance = 0;
bool BS = account.Value[0].balancesheetact;
for (int i = 1; i < 13; ++i)
{
ytdbalance = ytdbalance + account.Value[i].balance;
if (BS == true)
workbook.AddCell(row, i + 1, ytdbalance);
else
workbook.AddCell(row, i + 1, account.Value[i].balance);
}
}
Dictionaries are 1 to 1 maps, if you need a 1 to many map, you can create a Lookup.
Adding a duplicate to a dictionary will result in an ArgumentException.
It does overwrite. If you want to add the value to the existing one, I believe you're looking for:
if (dict.ContainsKey(key)) {
dict[key] += newVal;
} else {
dict[key] = newVal;
}
// or
int currentVal;
dict.TryGetValue(key, out currentVal);
dict[key] = currentVal + newVal;
You have to check to see if it exists first, then if it does, add the values and re-insert to the dictionary. If it doesn't exist, you can set it like normal.
Watch out for thread safety here - if your dictionary is accessed through multiple threads, this code could potentially get your dictionary out of whack.
If you want to just add values as you go along then instead of say Dictionary<String,String> you want Dictionary<String,List<String>> and instead of just calling add something like
if (!myDict.ContainsKey(someKey))
{
myDict.Add(someKey,new List<String>());
}
myDict[somekey].Add(somevalue);
one way to do it anyway.