Splitting List based on element variable and element position - c#

hey I'm trying to split a list based on if a bool of the element is true or not. but each time it has passed some true's and encounters a false i want it also to start a new list with the all the false until it encounters true again and so on. so basicly grouping sequences of falses and trues
public void SortWalls()
{
List<Node> innerWallNodes;
foreach(Wall w in walls)
{
WallAxis ax = w.axis;
innerWallNodes = new List<Node>();
for(int i=w.wallNodes.Count-1; i>=0; i--)
{
if(w.wallNodes[i].markedForDoor)
{
//split wall!!
innerWallNodes.Add(w.wallNodes[i]);
w.wallNodes.RemoveAt(i);
}
}
if(innerWallNodes.Count > 0)
{
Wall wall = new Wall(innerWallNodes, ax);
innerWalls.Add(wall);
}
}
}
i did it like this and then build a mesh based on the first and last element of a List. but since there are many scenarios where the innerWallNodes could be somewhere in the middle of the list that get "cut out" and so my remaining "outer wall" would still have the same node index in my grid for the first and last in it's list, still overdrawing my "inner wall"
so lets say every node !markedForDoor is 0 and a every node markedForDoor is 1 and they order something like below in my list.
like this:
|000|11111|00000|11|000| how would i get a list for every between |...| ?
how do i do this in a simple way. I thought Linq would have something for this but can't find anything.

Linq won't help. Here is the code:
List<List<YouObjectType>> SplitList(List<YourObjectType> listToSplit) {
List<List<YouObjectType>> listOfLists = new List<List<YourObjectType>>();
List<YourObjectType> tmp = new List<YourObjectType>();
foreach(YourObjectType item in listToSplit) {
if (tmp.Count > 0
&& tmp[tmp.Count - 1] != item) {
// Compare you items here as you wish,
// I'm not sure what kind of objects
// and what kind of comparison you are going to use
listOfLists.Add(tmp);
tmp = new List<YourObjectType>();
}
tmp.Add(item);
}
if (tmp.Count > 0) {
listOfLists.Add(tmp);
}
return listOfLists;
}

Here is a simple way of doing that (no Linq)
List<Node> input = ...;
var output = new List<List<Node>>();
for (int end = 0; end < input.Count; )
{
int start = end;
while (++end < input.Count && input[end].markedForDoor == input[start].markedForDoor) { }
output.Add(input.GetRange(start, end - start));
}

Lookup Group Results by Contiguous keys on MSDN.
See how it applies to your case on Rextester.

Related

How to remove overlapping rectangles (C#, System.Drawing) while keeping only the one with a certain attribute?

For a class that stores template matching results, I need to implement a efficent way of removing overlapping results (= overlapping bounding boxes, for e.g. this happens for low accuracy thresholds on template matching).
My Results variable in the example below is a dict<string, TemplateMatchingResult>, where the key is the name of the template and the value is aTemplateMatchingResult, which stores BoundingBoxes (actually List) and Scores (a List with the accuracy scores belonging to BoundingBoxes ).
In the first approach, I just compared them all by all and then removed the ones with lower score. But I often got IndexOutOfBoundsException for i2 here: this.Results[key2].Scores.RemoveAt(i2)
My second approach was to first store the indexes in a list (every index only once) belonging to the template name (used a dict for this: dict<string, List> ToDelete) and then remove them from outside of the loops and repeat this as long as it doesn't find any overlappings any more. But this also seems not very good, because the result still shows overlappings.
What would be the most efficient way to do this (Time Efficiency) ?
Here is my code for the removing:
public void RemoveOverlappingElements()
{
// Check dictionary for null or empty
if (this.Results != null && this.Results.Count != 0)
{
bool loopFlag = true;
while(loopFlag)
{
Dictionary<string, List<int>> toDelete = new Dictionary<string, List<int>>();
//Search for overlapping bounding boxes
foreach (string key1 in this.Results.Keys)
{
// Use a second loop variable for comparison to the first one
foreach (string key2 in this.Results.Keys)
{
// Only compare elements that are not the same
if (key1 != key2)
{
// Compare every bounding box of the element corresponding to key1 to every bonding box of the element corresponding to key1
for (int i1 = 0; i1 < this.Results[key1].BoundingBoxes.Count; i1++)
{
for (int i2 = 0; i2 < this.Results[key2].BoundingBoxes.Count; i2++)
{
//Check if they overlap, and store key and index for elements with the lowest accuracy score
if (this.Results[key1].BoundingBoxes[i1].GetRectangle().IntersectsWith(this.Results[key2].BoundingBoxes[i2].GetRectangle()))
{
if (this.Results[key1].Scores[i1] >= this.Results[key2].Scores[i2])
{
// Old Approach
//this.Results[key2].Scores.RemoveAt(i2);
//this.Results[key2].BoundingBoxes.RemoveAt(i2);
// New approach
// Only add keys that don't exist already and keep every index without duplicates
if (toDelete.Keys.Contains(key2))
{
toDelete[key2] = toDelete[key2].Union(new List<int> { i2 }).ToList();
}
else
{
toDelete.Add(key2, new List<int> { i2 });
}
}
else
{
//this.Results[key1].Scores.RemoveAt(i1);
//this.Results[key1].BoundingBoxes.RemoveAt(i1);
// Only add keys that don't exist already and keep every index without duplicates
if (toDelete.Keys.Contains(key1))
{
toDelete[key1] = toDelete[key1].Union(new List<int> { i1 }).ToList();
}
else
{
toDelete.Add(key1, new List<int> { i1 });
}
}
}
}
}
}
}
}
// Delete bounding boxes and scores for the overlapping elements
if (toDelete.Count() != 0)
{
foreach (var item in toDelete)
{
this.Results[item.Key].BoundingBoxes.RemoveAll(x => item.Value.Contains(this.Results[item.Key].BoundingBoxes.IndexOf(x)));
this.Results[item.Key].Scores.RemoveAll(x => item.Value.Contains(this.Results[item.Key].Scores.IndexOf(x)));
}
}
else
{
loopFlag = false;
}
}
}
}
An efficient algorithm to solve your problem:
Create an undirected graph in which the vertices are rectangles and there is an edge between two vertices if the corresponding rectangles intersect.
Find all connected components in your graph (with a depth-first search, for example).
Select the rectangle with maximum score from each connected component.

C# Bubble sorting Linked List for loop question

public void BubbleSortExData()
{
Node end = null;
Node p = null;
Node q = null;
for (end = null ; end != start.link; end = p)
{
for (p = start; p.link != end; p = p.link)
{
q = p.link;
if (p.data > q.data)
{
int temp = p.data;
p.data = q.data;
q.data = temp;
}
}
}
}
So I have this code, and it's working fine, but I am having hard time understanding it.
I get the part where we compare two int values, and we replace them if needed. But I don't understand two for loops.
As you say, the if statement swaps the data of 2 nodes if they are out of order.
The inner loop applies this if to each node from the beginning (start) until end, in that order. This means that the largest value will now be at the end.
The outer loop applies this process to successive ends, working from back to front. This means that with each iteration, the sorted portion of the list is a longer suffix of the it, until it covers the whole list.

Pass a array value to another array

I have a Array.It has for example 10 rows.
I need to checka flg. if flag has value false it goes to array number one,if it have flag true it goes go array number 2.I'm trying something like this.
if (lista2[i].FLG_FALHA == true)
{
listaInc[c] = lista2[i];
i++;
c++;
}
else
{
listaAlr[o] = lista2[i];
o++;
i++;
}
This is where i declare the arrays.
List<AcompanhamentoSiltIncDTO> lista2 = new List<AcompanhamentoSiltIncDTO>();
List<AcompanhamentoSiltIncDTO> listaInc = new List<AcompanhamentoSiltIncDTO>();
List<AcompanhamentoSiltIncDTO> listaAlr = new List<AcompanhamentoSiltIncDTO>();
I get this error,it's like the arrays are not initialized.
"{"The index was out of range, it should be non-negative and less than the size of the collection. \ R \ nName parameter: index"}"
You should call the Add() method of your list:
if (lista2[i].FLG_FALHA == true)
listaInc.Add(lista2[i]);
else
listaAlr.Add(lista2[i]);
Otherwise, since your listaAlr and listaInc have no element, you get cannot access element at position o : listaInc[o]
You get this error because you got out of range of your array. Check out your indexes. But for this task I suggest you working with linq. It gives you a lot of good functionality.
And if you have "List<>" you need to add elements to this list with "Add" method.
So code will be the next:
if (lista2[i].FLG_FALHA == true)
{
listaInc.Add(lista2[i]);
i++;
}
else
{
listaAlr.Add(lista2[i]);
i++;
}
But as I said you may use LinQ.
Code will be the next:
listInc = lista2.Where(x => x.FLG_FALHA);
listAlr = lista2.Where(x => !x.FLG_FALHA);
You can solve this with LINQ.
var listaInc = from n in lista2
where n == true
select n;
var listaAlr = from n in lista2
where n == false
select n;
Bits more, bits less, but that is what I would do.

Apply last non empty string to empty string?

A file is read in. Looks for lines that have a number that beings with an S The lines that do not have an S are maintained. Saves to an array. I am then populating an existing gridview with the same amount of lines.
As a place holder I have set the blank lines to *** This is where I'm stuck. I need the empty strings to be populated with the last non empty string.
So for example if the readout is:
1
2
3
Empty
Empty
Empty
4
Empty
6
I'd want it displayed as:
1
2
3
3
3
3
4
4
6
I can't figure out how to do that. I've been searching all day for examples but can only find ways of grabbing either the first or last number of my array is all. Here is my code.
var sLines = File.ReadAllLines(cboPartProgram.Text)
.Where(s => !s.StartsWith("'"))
.Select(s => new
{
SValue = Regex.Match(s, "(?<=S)[\\d.]*").Value,
})
.ToArray();
string LastSValue = "";
string Value = "";
for (int i = 0; i < sLines.Count(); i++)
{
if (sLines[i].SValue == "")
{
LastSValue = "***";
Value = LastSValue;
}
else
{
Value = (sLines[i].SValue);
}
}
Ok I think I got it.
for (int i = 0; i < sLines.Length; i++)
{
if (sLines[i].SValue == "" && i > 0)
{
foreach (var empt in sLines[i].SValue)
{
LastSValue = sLines[i - 1].SValue;
Value = LastSValue;
}
}
else
{
Value = (sLines[i].SValue);
}
On a side note, when I copy my code I use the code option above to format it, but I notice someone always has to correct my spacing. Its copied straight from the IDE but there are always spaced each line that I guess shouldn't be. Is there a different way I should do it?
UPDATE
If I should ask this as a new question let me know, but it's so dependent on this that I thought I should keep it here.
Using the code I posted above that does what I needed it too. I've been trying to edit this so that if there is NO previous number, so for example if there if line 1 has no number but the rest do, then just apply the string "NA" otherwise still do what the code above does to the rest of the lines.
I guess maybe the best way would be to just take the results from the above code, and if there are any empty spaces left, apply "NA" but I can't figure it out.
In your example, you just need to take the value of the row before to fill the current value. Something like the following :
for (int i = 0; i < sLines.Length; i++)
{
if (sLines[i].SValue == "" && i > 0)
{
sLines[i].SValue = sLines[i-1].SValue;
}
else
{
sLines[i].SValue = sLines[i].SValue;
}
}
Your example has one more issue but currently I'll focus only on gathering the "last non empty" string.
If you look at your example you can spot few things that could potentially help you finding solution. These are for loop and reference to original list that stays intact.
For my example I'll use Linq because it will be much easier.
First of all I'll copy all from before for loop ( if that makes sense :D ) :
var sLines = File.ReadAllLines(cboPartProgram.Text)
.Where(s => !s.StartsWith("'"))
.Select(s => new
{
SValue = Regex.Match(s, "(?<=S)[\\d.]*").Value,
})
.ToArray();
string LastSValue = "";
string Value = "";
Just because it's okay and will work for now.
With your for loop I'll make modifications :
for (int i = 0; i < sLines.Count(); i++)
{
// `i` is representing current "index" of processed "word"
// we can use this to find last "valid" element
// string notEmpty = sLines.Take(i).LastOrDefault(word => !string.IsNullOrEmpty(word));
// but since you want to assign this to `Value` and there can be not empty string at `i` index
// we can make it in one line :
Value = string.IsNullOrEmpty(sLines[i]) ? sLines.Take(i).LastOrDefault(word => !string.IsNullOrEmpty(word)) : sLines[i].SValue;
// instead of your previous logic :
//if (sLines[i].SValue == "")
//{
// LastSValue = "***";
// Value = LastSValue;
//}
//else
//{
// Value = (sLines[i].SValue);
//}
}
Another problem which I think you'll face is that first value ( judging by the input ) can also be empty. Which will throw exception in my example. This will also be impossible to fit this kind of solution because there's no previous value ( at all ).
From what I understand, if you want to store the result in Value and do something else with it inside the loop (instead of changing it in the array), what you probably want is this:
for (int i = 0; i < sLines.Count(); i++)
{
if (sLines[i].SValue == "")
{
Value = LastSValue;
}
else
{
Value = (sLines[i].SValue);
LastSValue = Value;
}
// use Value
}
I would also suggest using sLines.Length instead of Count(), which is made for sequences where the length isn't known in advance - it's supposed to literally count the elements one by one. In this case it would probably be optimized but if you know you're dealing with an array, it's a good idea to ask for the length directly.
EDIT:
To get "NA" if there's no previous number, just initialize LastSValue to this value before the loop:
string LastSValue = "NA";
That way, if Value is empty and there was not LastSValue set before, it will still be "NA".
EDIT2:
A solution similar to the one from #Cubi, to change it in place:
for (int i = 0; i < sLines.Length; i++)
{
if (sLines[i].SValue == "")
sLines[i].SValue = i > 0 ? sLines[i-1].SValue : "NA";
}

How to get last few element from foreach gameobject.transform

currently i do it manually by checking every loop of array with if statement
float addGap = gapFix;
foreach (Transform child in topViewPlan.transform)
{
if (child.name.Substring(5, 2).Equals("45") || child.name.Substring(5, 2).Equals("46") || child.name.Substring(5, 2).Equals("47") || child.name.Substring(5, 2).Equals("48"))
{
//rearrange child position
if (!child.name.Substring(5, 2).Equals("45"))
child.transform.localPosition += Vector3.right * addGap * 2;
else
child.transform.localPosition += Vector3.right * addGap;
}
}
is there any possibilities to get last few element of topViewPlan.transform ?
as an example letsay topViewPlan.transform consist of 10 element, and I want last 4 element (which is element 7,8,9 and 10) so maybe I could write :-
foreach (Transform child in topViewPlan.transform.getLast(4)){} //this is just example
so i could get last 4 element of the topViewPlan.transform
You can use GetComponentsInChildren< Transform >() which will give you and array with child and the object itself.
for exemple :
var childs = GetComponentsInChildren<Transform>();
int offset = 5; // want the five last elements
for(int i=childs.Length - offset; i<childs.Length; ++i)
{
// Do something with childs[i]
}
But I would assume that this code is inefficient and unstable, I can't assure that the array returned by GetComponentsInChildren is sorted in the right way (parent at 0 and childrens afterwards). A more sure and easy way would be that every child of your GameObject have a particular component, let's say :
class ChildrenIdentificator : MonoBehavior
{
public int id = 0;
}
You would set the id in every child so it have the behavior you want and then you can do :
var childs = GetComponentsInChildren<ChildrenIdentificator>();
for(int i=0 ; i<childs.Length ; ++i)
{
if(childs[i].id == // I let you figure out the rest
}
This way you have a better control and you can see directly what you are doing. You also avoid doing string comparison.
In any case I strongly recommand to not use GetComponentsInChildren at each frame, to solve that you can store the childs array in the Start() function before using it afterward.
Use
transform.childCount
to get number of childs then to get a child do this :
for example this will return child number 5 :
transform.GetChild(5);
the code you need :
int numberOfChildNeeded = 5;
for (int i = transform.childCount - 1; i > transform.childCount - numberOfChildNeeded ; i--)
{
//do something
transform.GetChild(i)
}

Categories

Resources