I want to add item which has only true value of NodeDir
public List<Node> BuildTreeHierarchy(List<Node> node, int? pKey)
{
if (node.Where(n => n.NodeDir.Equals(true)).Count() > 0)
{
return node.Where(n => n.ParentKey == pKey)
.Select(n => new Node()
{
ID = n.ID,
Name = n.Name,
Feature = n.Feature,
NodeDir = n.NodeDir,
ParentKey = n.ParentKey,
Left = BuildTreeHierarchy(node, n.ID)
}).ToList();
}
}
Result:
bear False
monkey True
wolf True
chicken False
stork False
Now in else part i get null exeption
*For example I get all items with false but with true no *
public List<Node> BuildTreeHierarchy(List<Node> node, int? pKey)
{
var nodesWithNodeDir = node.Where(n => n.NodeDir.Equals(false));
if (nodesWithNodeDir.Count() > 0)
{
return nodesWithNodeDir.Where(n => n.ParentKey == pKey)
.Select(n => new Node()
{
ID = n.ID,
Name = n.Name,
Feature = n.Feature,
NodeDir = n.NodeDir,
ParentKey = n.ParentKey,
Left = BuildTreeHierarchy(node, n.ID)
}).ToList();
}
else
{
return nodesWithNodeDir.Where(n => n.ParentKey == pKey)
.Select(n => new Node()
{
ID = n.ID,
Name = n.Name,
Feature = n.Feature,
NodeDir = n.NodeDir,
ParentKey = n.ParentKey,
Right = BuildTreeHierarchy(node, n.ID)
}).ToList();
}
Here is my logic where i need fill Left and Right Nodes from database
Storage data = new Storage();
public List<Node> nodes = new List<Node>();
public List<Node> AnimalTree = new List<Node>();
public List<Node> CreateTree()
{
foreach (DataRow animal in data.GetAnimals("select * from AnimalTbl").Rows)
{
Node newNode = new Node();
newNode.ID = Convert.ToInt32(animal["Id"]);
newNode.Name = animal["Name"].ToString();
newNode.Feature = animal["Feature"].ToString();
// newNode.NodeDir = animal["NodeDir"] == DBNull.Value ? (bool?)null : Convert.ToBoolean(animal["NodeDir"]);
newNode.NodeDir = Convert.ToBoolean(animal["NodeDir"]);
newNode.ParentKey = animal["ParentKey"] == DBNull.Value ? (int?)null : Convert.ToInt32(animal["ParentKey"]);
nodes.Add(newNode);
}
AnimalTree = BuildTreeHierarchy(nodes, 1);
return AnimalTree;
}
public List<Node> BuildTreeHierarchy(List<Node> node, int? pKey)
{
List<Node> aa = new List<Node>();
var nodesWithNodeDir = node.Where(n => n.NodeDir.Equals(true));
if (nodesWithNodeDir.Count() > 0)
{
return nodesWithNodeDir.Where(n => n.ParentKey == pKey)
.Select(n => new Node()
{
ID = n.ID,
Name = n.Name,
Feature = n.Feature,
NodeDir = n.NodeDir,
ParentKey = n.ParentKey,
Left = BuildTreeHierarchy(node, n.ID)
}).ToList();
}
nodesWithNodeDir = node.Where(n => n.NodeDir.Equals(false));
if (nodesWithNodeDir.Count() > 0)
{
return nodesWithNodeDir.Where(n => n.ParentKey == pKey)
.Select(n => new Node()
{
ID = n.ID,
Name = n.Name,
Feature = n.Feature,
NodeDir = n.NodeDir,
ParentKey = n.ParentKey,
Right = BuildTreeHierarchy(node, n.ID)
}).ToList();
}
else
{
return new List<Node>();
}
}
}
And here in button click event i need print right Node list where i get null exepction
protected void Button2_Click(object sender, EventArgs e)
{
cur++;
PrintTree(nd, cur);
}
private void PrintTree(IEnumerable<Node> nodes,int Current)
{
foreach (var root in nodes)
{
Response.Write(root.Name + " " +root.NodeDir + "<br/>");
PrintTree(root.Right, Current);
}
}
If I understand correctly, you're wondering why the list you generate isn't filtered by its NodeDir property. But your code never captures or uses the list that results from the Where method in the if statement. Try keeping that list around when you calculate it and use it for further filtering:
public List<Node> BuildTreeHierarchy(List<Node> node, int? pKey)
{
var nodesWithNodeDir = node.Where(n => n.NodeDir.Equals(true));
if (nodesWithNodeDir.Count() > 0)
{
return nodesWithNodeDir.Where(n => n.ParentKey == pKey)
.Select(n => new Node()
{
ID = n.ID,
Name = n.Name,
Feature = n.Feature,
NodeDir = n.NodeDir,
ParentKey = n.ParentKey,
Left = BuildTreeHierarchy(node, n.ID)
}).ToList();
}
}
foreach (DataRow animal in data.GetAnimals("select * from AnimalTbl").Rows)
{
Node newNode = new Node();
newNode.ID = Convert.ToInt32(animal["Id"]);
newNode.Name = animal["Name"].ToString();
newNode.Feature = animal["Feature"].ToString();
// newNode.NodeDir = animal["NodeDir"] == DBNull.Value ? (bool?)null : Convert.ToBoolean(animal["NodeDir"]);
newNode.NodeDir = Convert.ToBoolean(animal["NodeDir"]);
newNode.ParentKey = animal["ParentKey"] == DBNull.Value ? (int?)null : Convert.ToInt32(animal["ParentKey"]);
nodes.Add(newNode);
}
//AnimalTree = BuildTreeHierarchy(nodes, 1);
foreach (Node a in nodes)
{
List<Node> nodeSub = nodes.Where(n => n.ParentKey == a.ID).ToList<Node>();
a.Left = nodeSub.Where(n => n.NodeDir.Equals(false)).ToList<Node>();
a.Right = nodeSub.Where(n => n.NodeDir.Equals(true)).ToList<Node>();
}
return AnimalTree;
Not using function. Just a single foreach loop to assign Left and Right properties of each item. This is best for performance as well as simplicity.
Notice, when left or right nodes, this code gives you an empty collection, not null.
Related
I am looking for the best algorithm to compare 2 collections and determine which element got added and which element got removed.
private string GetInvolvementLogging(ICollection<UserInvolvement> newInvolvement, ICollection<UserInvolvement> oldInvolvement)
{
//I defined the new and old dictionary's for you to know what useful data is inside UserInvolvement.
//Both are Dictionary<int, int>, because The Involvement is just a enum flag. Integer. UserId is also Integer.
var newDict = newInvolvement.ToDictionary(x => x.UserID, x => x.Involvement);
var oldDict = oldInvolvement.ToDictionary(x => x.UserID, x => x.Involvement);
//I Want to compare new to old -> and get 2 dictionaries: added and removed.
var usersAdded = new Dictionary<int, Involvement>();
var usersRemoved = new Dictionary<int, Involvement>();
//What is the best algoritm to accomplish this?
return GetInvolvementLogging(usersAdded, usersRemoved);
}
private string GetInvolvementLogging(Dictionary<int, Involvement> usersAdded, Dictionary<int, Involvement> usersRemoved)
{
//TODO: generate a string based on those dictionaries.
return "Change in userinvolvement: ";
}
Added elements are only in newDict removed only in oldDict
var intersection = newDict.Keys.Intersect(oldDict.Keys);
var added = newDict.Keys.Except(intersection);
var removed = oldDict.Keys.Except(intersection);
EDIT
I modify your base function, dictionaries is no neded.
Example UserInvolvement implementation
class UserInvolvement
{
public int UserId;
public string Name;
public string OtherInfo;
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
UserInvolvement p = obj as UserInvolvement;
if ((System.Object)p == null)
{
return false;
}
return (UserId == p.UserId) && (Name == p.Name) && (OtherInfo == p.OtherInfo);
}
public override string ToString()
{
return $"{UserId} - {Name} - {OtherInfo}";
}
}
And example function:
private static string GetInvolvementLogging(ICollection<UserInvolvement> newInvolvement,
ICollection<UserInvolvement> oldInvolvement)
{
var intersection = newInvolvement.Select(x => x.UserId).Intersect(oldInvolvement.Select(x => x.UserId));
var addedIds = newInvolvement.Select(x => x.UserId).Except(intersection);
var removedIds = oldInvolvement.Select(x => x.UserId).Except(intersection);
List<UserInvolvement> modifiedUI = new List<UserInvolvement>();
foreach (var i in intersection)
{
var ni = newInvolvement.First(a => a.UserId == i);
var oi = oldInvolvement.First(a => a.UserId == i);
if (!ni.Equals(oi))
{
modifiedUI.Add(ni);
}
}
List<UserInvolvement> addedUI = newInvolvement.Where(x => addedIds.Contains(x.UserId)).Select(w => w).ToList();
List<UserInvolvement> removedUI = oldInvolvement.Where(x => removedIds.Contains(x.UserId)).Select(w => w).ToList();
StringBuilder sb = new StringBuilder();
sb.AppendLine("Added");
foreach (var added in addedUI)
{
sb.AppendLine(added.ToString());
}
sb.AppendLine("Removed");
foreach (var removed in removedUI)
{
sb.AppendLine(removed.ToString());
}
sb.AppendLine("Modified");
foreach (var modified in modifiedUI)
{
sb.AppendLine(modified.ToString());
}
return sb.ToString();
}
And my test function:
static void Main(string[] args)
{
List<UserInvolvement> newUI = new List<UserInvolvement>()
{
new UserInvolvement()
{
UserId = 1,
Name = "AAA",
OtherInfo = "QQQ"
},
new UserInvolvement()
{
UserId = 2,
Name = "BBB",
OtherInfo = "123"
},
new UserInvolvement()
{
UserId = 4,
Name = "DDD",
OtherInfo = "123ert"
}
};
List<UserInvolvement> oldUI = new List<UserInvolvement>()
{
new UserInvolvement()
{
UserId = 2,
Name = "BBBC",
OtherInfo = "123"
},
new UserInvolvement()
{
UserId = 3,
Name = "CCC",
OtherInfo = "QQ44"
},
new UserInvolvement()
{
UserId = 4,
Name = "DDD",
OtherInfo = "123ert"
}
};
string resp = GetInvolvementLogging(newUI, oldUI);
WriteLine(resp);
ReadKey();
WriteLine("CU");
}
Result is:
Added
1 - AAA - QQQ
Removed
3 - CCC - QQ44
Modified
2 - BBB - 123
You could try with Linq:
var usersAdded = newDict.Except(oldDict);
var usersRemoved = oldDict.Except(newDict);
If you need dictionaries as a result you can cast:
var usersAdded = newDict.Except(oldDict).ToDictionary(x => x.Key, x => x.Value);
var usersRemoved = oldDict.Except(newDict).ToDictionary(x => x.Key, x => x.Value);
Think best algorithm will be
foreach (var newItem in newDict)
if (!oldDict.ContainsKey(newItem.Key) || oldDict[newItem.Key]!=newItem.Value)
usersAdded.Add(newItem.Key, newItem.Value);
foreach (var oldItem in oldDict)
if (!newDict.ContainsKey(oldItem.Key) || newDict[oldItem.Key]!=oldItem.Value)
usersRemoved.Add(oldItem.Key, oldItem.Value);
Finally this is my implementation of GetInvolvementLogging:
(the implementation of the string builder method is irrelevant for my question here)
private string GetInvolvementLogging(ICollection<UserInvolvement> newInvolvement, ICollection<UserInvolvement> oldInvolvement)
{
//I defined the new and old dictionary's to focus on the relevant data inside UserInvolvement.
var newDict = newInvolvement.ToDictionary(x => x.UserID, x => (Involvement)x.Involvement);
var oldDict = oldInvolvement.ToDictionary(x => x.UserID, x => (Involvement)x.Involvement);
var intersection = newDict.Keys.Intersect(oldDict.Keys); //These are the id's of the users that were and remain involved.
var usersAdded = newDict.Keys.Except(intersection);
var usersRemoved = oldDict.Keys.Except(intersection);
var addedInvolvement = newDict.Where(x => usersAdded.Contains(x.Key)).ToDictionary(x => x.Key, x => x.Value);
var removedInvolvement = oldDict.Where(x => usersRemoved.Contains(x.Key)).ToDictionary(x => x.Key, x => x.Value);
//Check if the already involved users have a changed involvement.
foreach(var userId in intersection)
{
var newInvolvementFlags = newDict[userId];
var oldInvolvementFlags = oldDict[userId];
if ((int)newInvolvementFlags != (int)oldInvolvementFlags)
{
var xor = newInvolvementFlags ^ oldInvolvementFlags;
var added = newInvolvementFlags & xor;
var removed = oldInvolvementFlags & xor;
if (added != 0)
{
addedInvolvement.Add(userId, added);
}
if (removed != 0)
{
removedInvolvement.Add(userId, removed);
}
}
}
return GetInvolvementLogging(addedInvolvement, removedInvolvement);
}
Is it possible to include or exclude column within linq Select?
var numberOfYears = Common.Tool.NumberOfYear;
var list = users.Select(item => new
{
Id = item.Id,
Name= item.Name,
City= Item.Address.City.Name,
STATUS = Item.Status,
if(numberOfYears == 1)
{
Y1 = item.Records.Y1,
}
if(numberOfYears == 2)
{
Y1 = item.Records.Y1,
Y2 = item.Records.Y2,
}
if(numberOfYears == 3)
{
Y1 = item.Records.Y1,
Y2 = item.Records.Y2,
Y3 = item.Records.Y3,
}
}).ToList();
}
The idea is that i want to display Y1,Y2,Y3 only if has values
Thanks to the beauty of the dynamic keyword what you need is now possible in C#. Below an example:
public class MyItem
{
public string Name { get; set; }
public int Id { get; set; }
}
static void Main(string[] args)
{
List<MyItem> items = new List<MyItem>
{
new MyItem
{
Name ="A",
Id = 1,
},
new MyItem
{
Name = "B",
Id = 2,
}
};
var dynamicItems = items.Select(x => {
dynamic myValue;
if (x.Id % 2 == 0)
myValue = new { Name = x.Name };
else
myValue = new { Name = x.Name, Id = x.Id };
return myValue;
}).ToList();
}
This will return a list of dynamic objects. One with 1 property and one with 2 properties.
Try this approach:
var numberOfYears = Common.Tool.NumberOfYear;
var list = users.Select(item => new
{
Id = item.Id,
Name = item.Name,
City = Item.Address.City.Name,
STATUS = Item.Status,
Y1 = numberOfYears > 0 ? item.Records.Y1 : 0,
Y2 = numberOfYears > 1 ? item.Records.Y2 : 0,
Y3 = numberOfYears > 2 ? item.Records.Y3 : 0
}).ToList();
Instead of 0, add your default value or null.
Update:
According to your comments, the only option for you is to go dynamic. Here's example with dynamics:
var numberOfYears = 3;
var list = users.Select(x =>
{
dynamic item = new ExpandoObject();
item.Id = x.Id;
item.Name = x.Name;
item.Status = x.Status;
var p = item as IDictionary<string, object>;
var recordsType = x.Records.GetType();
for (var i = 1; i <= numberOfYears; ++i)
p["Y" + i] = recordsType.GetProperty("Y" + i).GetValue(x.Records);
return item;
}).ToList();
You can use the ExpandoObject like this:
var data = providers.Select(provider =>
{
dynamic excelRow = new ExpandoObject();
excelRow.FirstName = provider.FirstName ?? "";
excelRow.MiddleName = provider.MiddleName ?? "";
excelRow.LastName = provider.LastName ?? "";
// Conditionally add columns to the object...
if (someCondition)
{
excelRow.Property1ForCondition = provider.Property1ForCondition;
excelRow.Property2ForCondition = provider.Property2ForCondition;
}
excelRow.DueDate = provider.DueDate ?? null;
.
.
.
return excelRow;
});
Another variation of the above code can be:
var data = new List<ExpandoObject>();
providers.ForEach(provider =>
{
dynamic excelRow = new ExpandoObject();
excelRow.FirstName = provider.FirstName ?? "";
excelRow.MiddleName = provider.MiddleName ?? "";
excelRow.LastName = provider.LastName ?? "";
// Conditionally add columns to the object...
if (someCondition)
{
excelRow.Property1ForCondition = provider.Property1ForCondition;
excelRow.Property2ForCondition = provider.Property2ForCondition;
}
excelRow.DueDate = provider.DueDate ?? null;
.
.
.
data.Add(excelRow);
});
Just looking for some confirmation on this. I need to capture the previous and next IDs for my list. Is there a better way?
var questionArray = dc.Question
.Where(i => !i.IsDeleted)
.OrderBy(i => i.SortOrder)
.Select(i => new
{
i.QuestionID,
i.Name,
})
.ToArray();
var questionList = questionArray
.Select((item, index) => new
{
item.QuestionID,
PrevID = index > 0 ? questionArray[index - 1].QuestionID : (int?)null,
NextID = index < questionArray.Length - 1 ? questionArray[index + 1].QuestionID : (int?)null,
item.Name,
})
.ToList();
You could write a little helper extension to remove the need to have an array of results
public static IEnumerable<TResult> PrevNextZip<T, TResult>(this IEnumerable<T> stream, Func<T, T, T, TResult> selector) where T : class
{
using (var enumerator = stream.GetEnumerator())
{
if (enumerator.MoveNext())
{
T prev = null;
T curr = enumerator.Current;
while (enumerator.MoveNext())
{
var next = enumerator.Current;
yield return selector(prev, curr, next);
prev = curr;
curr = next;
}
yield return selector(prev, curr, null);
}
}
}
Then building your result would look like this
var questionList = questionArray.PrevNextZip((prev, item, next) => new
{
item.QuestionID,
PrevID = prev != null ? prev.QuestionID : (int?)null,
NextID = next != null ? next.QuestionID : (int?)null,
item.Name,
})
.ToList();
You could use Aggregate to build the list. The aggregate you build tracks the last element for setting PrevID in the next one and updates NextID of the last one as soon as a new one is inserted. About like this:
class Info { ... // QuestionID, PrevID, NextID, Name }
class ListBuilder { List<Info> list; Info lastElement; }
var questionList = dc.Question
.Where(i => !i.IsDeleted)
.OrderBy(i => i.SortOrder)
.Aggregate (
new ListBuilder () { list = new List<Info> (), lastElement = null },
(acc, source) => {
var lastElement = acc.lastElement;
var newElement = new Info () {
QuestionID = source.QuestionID,
PrevID = lastElement != null ? lastElement.QuestionID : null,
NextID = null
};
acc.list.Add (newElement);
if (lastElement != null) lastElement.NextID = source.QuestionID;
acc.lastElement = newElement;
return acc;
})
.list;
This preserves some "spirit" of functional programming, but you could as well simply use a closure and track the necessary information in a local variable defined just before your linq query.
Or build a function for it:
IEnumerable<Info> previousNextInfo (IEnumerable<QuestionType> sourceEnumerable) {
Info preprevious = null;
Info previous = null;
Info current = null;
foreach (Info newOne in sourceEnumerable) {
preprevious = previous;
previous = current;
current = newOne;
yield return new Info () {
QuestionID = previous.ID,
lastID = preprevious != null ? preprevious.ID : null,
nextID = current.ID
};
}
if (current != null)
yield return new Info () { QuestionID = current.ID, lastID = previous.ID, nextID = null };
}
...
questionList = previousNextInfo (dc.Question
.Where(i => !i.IsDeleted)
.OrderBy(i => i.SortOrder)
).ToList ();
Take a look at my code here:
public static ItemType GetItem(int id)
{
ItemType it = new ItemType();
using (var context = matrix2.matrix2core.DataAccess.Connection.GetContext())
{
var q = (from ci in context.Item
where ci.ID == id
let TemplateID = ci.TemplateID
let Groups = from x in context.CriteriaGroup
where x.TemplateID == TemplateID
select new
{
x
}
let CriteriaItems = from x in context.CriteriaItem
where Groups.Select(y => y.x.ID).Contains(x.CriteriaGroupID)
select new
{
x
}
select new
{
ci.ID,
ci.Name,
ci.CategoryID,
ci.Description,
ci.ItemValue,
TemplateID,
Groups,
CriteriaItems,
ItemValues = from x in context.ItemValue
where x.ItemID == id
select new
{
x,
CriteriaID = x.CriteriaItem.Criteria.ID
}
}).FirstOrDefault();
if (q != null)
{
it.ID = q.ID;
it.CategoryID = q.CategoryID;
it.Name = q.Name;
it.TemplateID = q.TemplateID;
it.Description = q.Description;
it.CriteriaGroups = new List<CriteriaGroupType>();
it.CriteriaItems = new List<CriteriaItemType>();
it.ItemValues = new List<ItemValueType>();
foreach (var x in q.ItemValues)
{
ItemValueType ivt = new ItemValueType();
ivt.CriteriaItemID = x.x.CriteriaItemID;
ivt.CriteriaID = x.CriteriaID;
ivt.Data = x.x.Data;
ivt.ID = x.x.ID;
ivt.ItemID = x.x.ItemID;
it.ItemValues.Add(ivt);
}
/////////error when I added the orderby clause
foreach (var x in q.Groups.OrderBy(x => x.x.SortOrder))
{
CriteriaGroupType cgt = new CriteriaGroupType();
cgt.ID = x.x.ID;
cgt.Name = !string.IsNullOrEmpty(x.x.Name) ? x.x.Name : "Group" + x.x.ID;
cgt.SortOrder = x.x.SortOrder;
cgt.TemplateID = x.x.TemplateID;
it.CriteriaGroups.Add(cgt);
}
/////////error when I added the orderby clause
foreach (var temp in q.CriteriaItems.OrderBy(x => x.x.SortOrder))
{
CriteriaItemType cit = new CriteriaItemType();
cit.ID = temp.x.ID;
cit.CriteriaGroupID = temp.x.CriteriaGroupID;
cit.GroupName = (temp.x.Name != null) ? temp.x.Name : "Group" + temp.x.ID;
cit.CriteriaID = temp.x.CriteriaID;
cit.CriteriaName = temp.x.Criteria.Name;
cit.Name = !string.IsNullOrEmpty(temp.x.Name) ? temp.x.Name : temp.x.Criteria.Name;
cit.Options = temp.x.Options;
it.CriteriaItems.Add(cit);
}
}
}
return it;
}
Instead of letting SQL handle the sorting (OrderBy) I wanted asp.net to do the sorting instead. I took the sorting out of the SQL linq query and put it on the foreach loop. When I did that I got the error. Is there a way to fix this?
You should be able to go from IQueryable to IEnumerable with a simple
var q2 = q.ToList();
What I meant of course was :
var groups = q.Groups.ToList();
I currently have the following:
public IEnumerable<News> NewsItems
{
get { return from s in News.All() where s.Description.Contains(SearchCriteria) || s.Summary.Contains(SearchCriteria) select s; }
}
The problem is I only need to return the one property that actually has the data as well as the Title property, something similar to.
return from s in News.All() where s.Description.Contains(SearchCriteria) || s.Summary.Contains(SearchCriteria) select new {Title = s.Title, Data = //Description or Summary containing the data
How do I determine which one contains the search query?
UPDATE: I have this but it obviously hits the DB 3 times
var FoundInSummary = News.All().Any(x => x.Summary.Contains(SearchCriteria));
var FoundInDesc = News.All().Any(x => x.Description.Contains(SearchCriteria));
IEnumerable<NewsEventSearchResults> result = null;
if ((FoundInSummary && FoundInDesc) || (FoundInSummary))
{
result = (from s in News.All() where s.Summary.Contains(SearchCriteria) select new NewsEventSearchResults { Title = s.Title, Data = s.Summary, ID = s.ID }).AsEnumerable();
}
else if (FoundInDesc)
{
result = (from s in News.All() where s.Description.Contains(SearchCriteria) select new NewsEventSearchResults { Title = s.Title, Data = s.Description, ID = s.ID }).AsEnumerable();
}
return result;
UPDATE 2: Is this more efficent?
var ss = (from s in News.All() where s.Description.Contains(SearchCriteria) || s.Summary.Contains(SearchCriteria) select s).ToList();
List<NewsEventSearchResults> resultList = new List<NewsEventSearchResults>();
foreach (var item in ss)
{
bool FoundInSummary = item.Summary.Contains(SearchCriteria);
bool FoundInDesc = item.Description.Contains(SearchCriteria);
if ((FoundInSummary && FoundInDesc) || (FoundInSummary))
{
resultList.Add(new NewsEventSearchResults { Title = item.Title, Data = item.Summary, ID = item.ID });
}
else if (FoundInDesc)
{
resultList.Add(new NewsEventSearchResults { Title = item.Title, Data = item.Description, ID = item.ID });
}
}
What if they both contain the criteria? Or are they mutually exclusive? If so
Data = (s.Description != null ? s.Description : s.Summary)
I went with option 3