Algorithm find every child by idParent property - c#

I have a table named Drinking.
Here is how it looks:
Name idCategory idParentCategory
drink 1 1
alcohol 2 1
nonalcohol 3 1
tea 5 3
juice 4 3
sparkling 6 4
nonsparkling 7 4
pepsi 8 6
schweppes 9 6
wine 10 2
beer 11 2
Now, the input is idCategory. As you can see there isn't property idChildren. What I am trying to do is find the ids of the children.
For example, if the input is 1, the output should be 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11.
Here is what I tried:
public void myMethod()
{
List<Drinking> drinkingList= (from d in myEntities.Drinking
select d).ToList();
foreach (var d in drinkingList)
{
if (d.Drinking2Reference.EntityKey != null)
{
s = c.Drinking2Reference.EntityKey.EntityKeyValues[0].Value.ToString();
idPD = Int32.Parse(s);
//get idParent
if (idPC == idCat)
{
//if idParent is equal as the input, put this idCategory
//in a list of integers.
//Now, here comes the tricky part.
//I should continue with this loop AND repeat this for
//every child of idPC.
//Where to put the call for this method?
//Where to put the return statement?
//Here is what I'm doing
myMethod(idPC);
}
else
{
myMethod(idPC);
}
}
}
}
My goal is to fill a list with the ids of the children and greatchildren

public void myMethod(int value)
{
List<int> intList = new List<int>();
List<Drinking> drinkingList= (from d in myEntities.Drinking
select d).ToList();
foreach (var d in drinkingList)
{
if (d.Drinking2Reference.EntityKey != null)
{
if(d.idCategory == value)
intList.Add(value);
else {
for(int i=0; i < intList.Count; i++) {
if(intList.ElementAt(i) == d.idParentCategory)
intList.Add(d.idCategory);
}
}
}
}
//Print numbers
}
I haven't tested this, but I feel like it should work. Feel free to comment if you catch something. Also, the table has to be sorted by idParentCategory for this to work. I will also say that with this situation I would recommend you look into using trees rather than a table.

Related

C# - Merge list items into one item based on some matching values

I have a list of items (List<Tasks> tasks), like this one:
Id Action Source Target
------- --------- -------- ---------
1 Save 12 18
4 Save 18 21
7 Save 21 23
6 Save 23 25
10 Save 25 27
16 Save 29 31
0 Edit 31 37
What I want to do, is to merge the rows that have the same (Source and Target) and the same Action. For example, what I need at the end should look like this:
Id Action Source Target
------- --------- -------- ---------
22 Save 12 27
16 Save 29 31
0 Edit 31 37
Which means, all the items that have the same Action(in my case here Save) should be merged to one row/item but only in case the Target value of the upper item is equal to the Source value of the follower item. A follower is the lower item.
For example, upper is item Id = 1 and lower/follower is item Id = 4. So the record that follows.
Is there any way to do this with linq avoiding the too many foreach loops?
Maybe something like Hierarchies with CTE in SQL. But I'm still not finding the correct syntax, so that's why I didn't paste any code.
Thanks in advance!
If you want "LINQ" solution, you can use Aggregate like this:
var result = tasks.Aggregate(new List<Item>(), (acc, current) =>
{
if (acc.Count > 0)
{
var prev = acc[acc.Count - 1];
if (prev.Action == current.Action && prev.Target == current.Source)
{
// update previous target
prev.Target = current.Target;
}
// otherwise just add
else acc.Add(current);
}
else acc.Add(current);
return acc;
});
It starts with empty List as accumulator, and feeds items one by one. Then we just add items to accumulator if they do not match criteria, and if they do match - we update previous item instead.
Take a look at MoreLinq. There is a function named Segment which splits the sequence into subsequences based on some condition:
var grouped = tasks
.GroupBy(t => t.Action, (k, g) => g
.Segment((s, f, a) => s.Source != f.Target)
.Select(c => new
{
c.First().Source,
c.Last().Target,
Action = k
})));
So the sequence is divided and a new subsequence is created on each adjacent pair when s.Source != f.Target (f is first element and s is second in a pair).
Something like a CTE query. First select seed nodes (no records pointing to the SOURCE) then in the do-wile loop change Targets to get the end target of each chain. No previous order is required.
public class Tasks
{
public int Id;
public string Action;
public int Source;
public int Target;
}
static void Main(string[] args)
{
List<Tasks> tasks = new List<Tasks>{
new Tasks{Id=1,Action="Save",Source= 12,Target=18},
new Tasks{Id=4,Action="Save",Source= 18,Target=21},
new Tasks{Id=7,Action="Save",Source= 21,Target=23},
new Tasks{Id=6,Action="Save",Source= 23,Target=25},
new Tasks{Id=10,Action="Save",Source= 25,Target=27},
new Tasks{Id=16,Action="Save",Source= 29,Target=31},
new Tasks{Id=0,Action="Edit",Source= 31,Target=37},
};
var collectTasks = (from t in tasks
where !tasks.Any(t1 => (t1.Target == t.Source)&&(t1.Action == t.Action)&&(t1.Id!=t.Id))
select t).ToList();
foreach (var ct in collectTasks)
{
do{
var t1 = from t in tasks where ((ct.Target == t.Source)&&(ct.Action == t.Action)&&(ct.Id!=t.Id)) select t;
if (t1.Count() == 0) { break; }
ct.Target = t1.First().Target;
} while (true);
}
foreach (var t in collectTasks)
{
Console.WriteLine("Action = {0}, Source = {1}, Target = {2}", t.Action, t.Source, t.Target);
}
}
You need just one loop using index to access list items, starting from 1 and combining current item with previous if their Action match and Target of previous item is equal to Source of current:
for (int i = 1; i < items.Count; i++)
if (items[i].Action == items[i - 1].Action && items[i].Source == items[i - 1].Target)
{
items[i - 1].Target = items[i].Target;
items.RemoveAt(i);
i--; // to have same index after i++
}
That would not change Id, so it will be 1 and not 22 as you wrote.
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication33
{
class Program
{
static void Main(string[] args)
{
List<Task> tasks = new List<Task>() {
new Task() { Id = 1, Action = "Save", Source = 12, Target = 18},
new Task() { Id = 4, Action = "Save", Source = 18, Target = 21},
new Task() { Id = 7, Action = "Save", Source = 21, Target = 23},
new Task() { Id = 6, Action = "Save", Source = 23, Target = 25},
new Task() { Id = 10, Action = "Save", Source = 25, Target = 27},
new Task() { Id = 16, Action = "Save", Source = 29, Target = 31},
new Task() { Id = 0, Action = "Edit", Source = 31, Target = 37}
};
for(int i = tasks.Count - 1; i >= 0; i--)
{
int source = tasks[i].Source;
List<int> match = tasks.Select((x, index) => new { x = x, i = index }).Where(x => (x.x.Target == source) && (tasks[i].Action == tasks[x.i].Action)).Select(x => x.i).ToList();
if (match.Count > 0)
{
tasks[match[0]].Target = tasks[i].Target;
tasks.RemoveAt(i);
}
}
}
}
public class Task
{
public int Id { get; set; }
public string Action { get; set; }
public int Source { get; set; }
public int Target { get; set; }
}
}
This procedure will do what you need in one-pass, preserving the "Id" values of items that can't be merged.
You'll see it works on an ordered list so strictly speaking there is some LINQ-hidden activity there; but generally speaking you'll find that LINQ queries incur an overhead which make then always slower than for-loops over arrays.
private List<Task> Merge(List<Task> list)
{
var result = new List<Task>();
var maxId = list.Max(x => x.Id) + 1;
// order list for this algo to work
var listO = list.OrderByDescending(x => x.Action).ThenBy(x => x.Source).ToArray();
// create seed and counter
Task seed = listO[0];
var ctr = 0;
for (var i = 0; i < listO.Length - 1; i++)
{
if (listO[i + 1].Source == listO[i].Target && listO[i + 1].Action == listO[i].Action && listO[i + 1].Action == seed.Action)
{
// if the next is the last, merge it now
if (i + 1 == listO.Length - 1)
result.Add(new Task() { Id = maxId++, Action = seed.Action, Source = seed.Source, Target = listO[i].Target });
// the next item qualifies for merge, move to next
ctr++;
continue;
}
// next item does not qualify for merge, merge what we have or just add the item if ctr == 0
result.Add(ctr == 0 ? seed : new Task() { Id = maxId++, Action = seed.Action, Source = seed.Source, Target = listO[i].Target });
// reset seed record + counter
seed = listO[i+1];
ctr = 0;
// if the next item is the last, it belongs in the list as is
if (i + 1 == listO.Length - 1) result.Add(seed);
}
return result;
}

How do I show the path of Bfs?

So I'm trying to make BFS algorithm, and I was able to calculate the shortest path distance between each 2 nodes. But the neighbors of each node (i.e node A) is not only nodes it's a dictionary of nodes as a key and a hashset of matches where each two nodes played in. Now, I don't know how to store the path while the BFS is working... This the adjacency function that returns the neighbors of each nodes
Dictionary<string, Dictionary<string, HashSet<string>>> dic= new Dictionary<string, Dictionary < string,, HashSet < string >>>;
public IEnumerable<KeyValuePair<string, HashSet<string>>> adjacentto(string v)
{
return dic[v];
}
And this is my BFS function:
private Dictionary<string, int> dist = new Dictionary<string, int>();
public void BFSDegree(Graph g, string s, string p)
{
Queue<string> q = new Queue<string>();
dist.Add(s, 0);
q.Enqueue(s);
while (q.Count() != 0)
{
string j = q.Dequeue();
//count = 0;
foreach (KeyValuePair<string, HashSet<string>> h in g.adjacentto(j))
{
if (!dist.ContainsKey(h.Key))
{
q.Enqueue(h.Key);
dist.Add(h.Key, 1 + dist[j]);
}
if (j == p)
{
Console.WriteLine(" " + dist[j]);
return;
}
}
}
}
So, what I need it is to go see the path and read the values of the hashset, for example node A and node B played together in 3 matches match 1, match 2, match 7 so this should be the path. So I'm gonna print to the console "The path is either match 1, match 2 or match 7). And the same thing if I had 2 nodes that didn't star in a match together but they both starred with node A in 2 separate matches so the path should be through either of these 2 matches. How do I keep track of the paths and store the path while operating BFS? This is the file I'm reading the graph from.
This is the how I built my graph
And this is what I want to achieve
I was able to achieve the first goal (degree) through using BFS. But now I don't know how to achieve the "chain" or the path. The chain is nothing but the number of the movies in the path so I think if I'm able to save the path (show the path) while the BFS is working I shall be able to achieve the chain. So my problem is the last goal how do I save the path and show it.
In order to find the shortest path from one node to another, you can keep track of parents of each node. for example the graph below, when I run bfs from 0 up to 9, I keep track of each node reached an assign a parent. Once a parent is assigned, I do not reassign. So for example, if I want to find path from 0 to 9 and length I simply backtrack i.e. 9-7-3-1-0 so start from 9's parent and check 7's parent and so forth until you get to start node. We can easily then get the length.
As for the query, when you do something like C/E you can first run bfs to check for the path "which should be 2 going from C-A-E" and of course there could be other paths but I guess shortest path is the best for what you want ?
Anyway, let's assume we choose path C-A-E we can update the Rel. by the number of edges so Rel = 2 and then Chain will be
//Nodes -> [C, A, E]
//Parents ->[C, C, A]
Start from E // which is destination
Get movies of parent // parent is A, returns Movies 2
move to parent // now we are checking A
Get movies of parent // parent is C, returns Movies 1 Movies 7
break;
You break as soon as you reach source or you can do it vice versa
At the end you have Movies 2,1, and 7
A parent is just a predecessor of a node. For example, while you run Bfs if you go from 0 to 1 then 0 is parent of 1
Here is an implementation that I hope will help you understand it a little better.
private Map<Integer, List<Integer>> _adjacencyList;
private Map<Integer, String> _movies; // will store neighbors here
private Queue<Integer> queue;
private int [] visited;
public BaconNumber(int V){// v here is number of vertices
_adjacencyList = new HashMap<Integer, List<Integer>>();
_movies = new HashMap<Integer, String>();
queue = new LinkedList<Integer>();
visited = new int[V];
for(int i = 0; i < V; i++){
_adjacencyList.put(i, new LinkedList<Integer>());// add a linkedlist for each vertex
visited[i] = -1;
}
}
Fill movies here
private void fillNeighbors(){
//0 = A, 1 = B, 2 = C, 3 = D, 4 = E
_movies.put(0, "Z Movie 0 | B Movie 1 Movie 2 Movie 7 | C Movie 1 Movie 7 | D Movie 2 Movie 7 | E Movie 2");
_movies.put(1, "A Movie 1 Movie 2 Movie 7 | C Movie 1 Movie 7 | D Movie 2 Movie 7 | E Movie 2");
_movies.put(2, "A Movie 1 Movie 7 | B Movie 1 Movie 7 | D Movie 7");
_movies.put(3, "E Movie 2 | A Movie 2 Movie 7 | B Movie 2 Movie 7 | C Movie 7");
_movies.put(4, "D Movie 2 | A Movie 2 | B Movie 2 | F Movie 3 | G Movie 3");
}
Get movies. This takes in two parameters. One for where we get the movie from and the other for the node we are looking for. Note that I converted the second parameter to a letter so it looks like what you have
public String getMovies(int s, int v){
String result = "";
// just getting corresponding character
int rem = v % 26;
char l = (char)((int)'A' + rem);
//System.out.println("this is char "+l);
String movie = _movies.get(s);
String [] tokens = movie.split("\\|");
for(int i = 0; i < tokens.length; i++){
String next = tokens[i];
if(next.contains(String.valueOf(l))){
result = next;
break;
}
}
return result;
}
And now the query part
String query(int source, int dest){
List<Integer> nodes = new LinkedList<Integer>();
int i, element;
visited[source] = source;
queue.add(source);
while(!queue.isEmpty()){
element = queue.remove();
i = element;
if(i == dest) break; // we stop as soon as we reach destination
nodes.add(element);
List<Integer> iList = getEdge(i);
System.out.println(i+" -> "+iList);
int x = 0;
while(x < iList.size()){
int index = iList.get(x);
if(visited[index] == -1){
queue.add(index);
visited[index] = i;
}
x++;
}
}
String result = "";
for(int x = dest; x >= 0; x= visited[x]){
if(x == source) break; // we are done
if(visited[x] != -1){
result += getMovies(x,visited[x]); // get predecessor of x movies from x
}
}
return result;
}

Loop Through Integer Array

What I am trying to do is loop through an integer array
int[] integerarray = { 1, 2, 3, 4 };
for (??????)
{
// What do I do here?
}
Until I get to 3. I'm not sure how though.
we can achieve this by using simple for each loop
foreach(int i in integerarray)
{
if(i==3)
{
// do your stuf here;
break;
}
}
int[] integerarray = { 1, 2, 3, 4 };
for (int i=0;i<integerarray.Length;i++)
{
if(integerarray[i]==3)
break;
//Do something here
}
One way to do a loop of a fixed number is a while loop.
int counter = 0;
while(counter < 3)
{
tmp = integerArray[counter];
????
counter++;
}
You can use linq TakeWhile method to get the elements from a sequence as long as a specified condition is true.
Here you want that return element untill we found 3 in the sequence so you can write this statement like this
var result = integerarray.TakeWhile(x => !x.Equals(3)).ToList();
result will contains the items which comes before 3
in your case result will have 1,2
Use LINQ.
int[] integerarray = { 1, 2, 3, 4 };
for (var number = integerarray.TakeWhile(x => x != 3))
{
// Do something
}

Singly linked code explanation

I came across this singly linked list implementation in a book. However, I don't understand some statements. My questions against each statement are listed against each statement.
1 class Node {
2 Node next = null; //Is this a constructor?
3 int data;
4 public Node(int d) { data = d; }
5 void appendToTail(int d) {
6 Node end = new Node(d);
7 Node n = this;//Why is "this" keyword used here? What does this do?
8 while (n.next != null) { n = n.next; }//Where does "next" member come from?
9 n.next = end;
10 }
11 }
2 Node next = null; //Is this a constructor? NO it is not.
4 public Node(int d) { data = d; } //This is constructor
7 Node n = this; //The this keyword refers to the current instance of the class
8 while (n.next != null) { n = n.next; } //Learn Linked list

Custom Sorting based on predefined keys

I need to sort an employee list based on predefined uniqueIds.
In simple words, consider a list of employee Ids 1 to 10 in random order.
I have a predefined rule that says order employee objects in 2, 8, 1, 4, 6 And if any employee UId is not in range [1,10] put them at the end of list...(any order).
I wrote following code using IComparer<Employee>.
public class Employee
{
public int UId { get; set; }
public string Name { get; set; }
}
class Comparision : IComparer<Employee>
{
List<int> referenceKeys = new List<int> { 2, 8, 1, 4, 6 };
public int Compare(Employee thisOne, Employee otherOne)
{
var otherIndex = referenceKeys.IndexOf(otherOne.UId);
var thisIndex = referenceKeys.IndexOf(thisOne.UId);
if (thisIndex > otherIndex)
{
return 1;
}
else if (thisIndex < otherIndex)
{
return -1;
}
else
{
//if uid not found in reference list treat both employee obj as equal
return 0;
}
}
}
class CustomSorting
{
public static
List<Employee> employees = new List<Employee>
{
new Employee{UId=1, Name="Ram"},
new Employee{UId=2 , Name="Shyam"},
new Employee{UId=3 , Name="Krishna"},
new Employee{UId=4 , Name="Gopal"},
new Employee{UId=5 , Name="Yadav"},
new Employee{UId=6 , Name="Vishnu"},
new Employee{UId=7 , Name="Hari"},
new Employee{UId=8 , Name="Kanha"},
};
void sort()
{
employees.Sort(new Comparision());
}
static void Main()
{
new CustomSorting().sort();
}
}
I have been able to sort the list, with following result-
(5, 7, 3), 2, 8, 1, 4, 6 ==> 5, 7, 3 are not listed in reference key, so should appear in last, any order..
But items not found in my reference keys, are sorted first. I need to put them at the end.
For such a scenario, is IComparer, best way to go for ?
var otherIndex = referenceKeys.IndexOf(otherOne.UId); will return -1 if the item isn't found, which will be less than any found value.
You want all not found items to be greater than any found value, so just add:
if(otherIndex == -1) otherIndex = int.MaxValue;
if(thisIndex == -1) thisIndex = int.MaxValue;
On a side note, you can simplify the remainder of the method by just using:
return thisIndex.CompareTo(otherIndex);

Categories