Related
Suppose the list is like a-a-a-b-b-a-c-c-c-a-a, how do I get the groups
{a-a-a}-{b-b}-{a}-{c-c-c}-{a-a}?
So not what I want is: {a-a-a-a-a-a}-{b-b}-{c-c-c}.
Try following code;
var list = new List<string>
{
"a","a","a","b","b","a","c","c","c","a","a"
};
int index = 0;
string lastItem = "";
var groupedList =
list
.Select(x =>
{
if (lastItem != x)
{
index++;
lastItem = x;
}
return new { Item = x,Index = index };
})
.GroupBy(grp => grp)
.Select(grp => grp.Select(x => x.Item).ToList())
.ToList();
Output
a,a,a
b,b
a
c,c,c
a,a
public static List<(string key, List<T> items)> GroupByConsecutive<T>(
IEnumerable<T> items,
Func<T, string> key)
{
var list = new List<(string key, List<T> items)>();
foreach (var item in items)
{
if (list.Count == 0 || list.Last().key != key(item))
{
list.Add((key(item), new List<T> { item }));
}
else
{
list.Last().items.Add(item);
}
}
return list;
}
I have the following table structure
ID firstName LastName zip Address
1 test1 test2 NULL NULL
2 test1 test2 12345 MI
I need to merge 2 accounts (primary & secondary) depending on IDs provided. For example, if I am given the values 1 (as primary) and 2 (as secondary) to merge.
The primary account (1) has NULL zip and Address so i need to copy those from secondary account (2) and update. The final result should be
ID firstName LastName zip Address
1 test1 test2 12345 MI
Is there any way to do using Linq or can another approach be recommended?
Though there is no native merge operator in LINQ you have a few options.
First, create your own merge!
public Account Merge(Account one, Account two) {
ret = new Account(){
Field = one.Field??two.Field
//Repeat for all fields
};
}
Then use (handwritten code, don't mind syntax errors)
var mergedResults = (from primary in primaryAccounts
join secondary in secondaryAccounts
on primary.Id equals secondary.Id
select new {Primary=primary, Secondary secondary})
.Select(x=>Merge(x.Primary,x.Secondary);
Second, do merge in the LINQ
Not differs much
var mergedResults = (from primary in primaryAccounts
join secondary in secondaryAccounts
on primary.Id equals secondary.Id
select new Account { Field = primary.Field??secondary.Field}; //Repeat for all fields
Mabe with my extension
public interface IMerge<out T>
{
IEnumerable<IMergeMatched<T>> Matched();
IEnumerable<IMergeMatched<T>> Matched(Func<T, T, bool> predicate);
IEnumerable<T> NotMatchedBySource();
IEnumerable<T> NotMatchedBySource(Func<T, bool> predicate);
IEnumerable<T> NotMatchedByTarget();
IEnumerable<T> NotMatchedByTarget(Func<T, bool> predicate);
}
public interface IMergeMatched<out T>
{
T Source { get; }
T Target { get; }
}
public static class Enumerable
{
public static IMerge<TSource> Merge<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> target,
Func<TSource, TSource, bool> predicate)
{
return new Merge<TSource>(source, target, predicate);
}
}
public class Merge<T> : IMerge<T>
{
private readonly Func<T, T, bool> _predicate;
private readonly IEnumerable<T> _source;
private readonly IEnumerable<T> _target;
private IEnumerable<IMergeMatched<T>> _matcheds;
private IEnumerable<T> _notMatchedBySource;
private IEnumerable<T> _notMatchedByTarget;
public Merge(IEnumerable<T> source, IEnumerable<T> taget, Func<T, T, bool> predicate)
{
_source = source;
_target = taget;
_predicate = predicate;
}
public IEnumerable<IMergeMatched<T>> Matched()
{
if (_matcheds == null)
{
Analize();
}
return _matcheds;
}
public IEnumerable<IMergeMatched<T>> Matched(Func<T, T, bool> predicate)
{
return Matched()
.Where(t => predicate.Invoke(t.Source, t.Target))
.ToArray();
}
public IEnumerable<T> NotMatchedBySource()
{
if (_notMatchedBySource == null)
{
Analize();
}
return _notMatchedBySource;
}
public IEnumerable<T> NotMatchedBySource(Func<T, bool> predicate)
{
return NotMatchedBySource()
.Where(predicate)
.ToArray();
}
public IEnumerable<T> NotMatchedByTarget()
{
if (_notMatchedByTarget == null)
{
Analize();
}
return _notMatchedByTarget;
}
public IEnumerable<T> NotMatchedByTarget(Func<T, bool> predicate)
{
return NotMatchedByTarget()
.Where(predicate)
.ToArray();
}
private void Analize()
{
var macheds = new List<MergeMached<T>>();
var notMachedBySource = new List<T>(_source);
var notMachedByTarget = new List<T>(_target);
foreach (var source in _source)
{
foreach (var target in _target)
{
var macth = _predicate.Invoke(source, target);
if (!macth) continue;
macheds.Add(new MergeMached<T>(source, target));
notMachedBySource.Remove(source);
notMachedByTarget.Remove(target);
}
}
_matcheds = macheds.ToArray();
_notMatchedBySource = notMachedBySource.ToArray();
_notMatchedByTarget = notMachedByTarget.ToArray();
}
}
public class MergeMached<T> : IMergeMatched<T>
{
public MergeMached(T source, T target)
{
Source = source;
Target = target;
}
public T Source { get; private set; }
public T Target { get; private set; }
}
How to use?
[TestMethod]
public void TestMerge()
{
var source = new List<MediaFolder>
{
new MediaFolder
{
Id = "Id1",
Name = "Name1",
Path = "Path1"
},
new MediaFolder
{
Id = "Id2",
Name = "Name2",
Path = "Path2"
},
new MediaFolder
{
Id = "Id3",
Name = "Name3",
Path = "Path3"
},
new MediaFolder
{
Id = "Id4",
Name = "Name4",
Path = "Path4"
},
new MediaFolder
{
Id = "Id5",
Name = "Name5",
Path = "Path5"
},
new MediaFolder
{
Id = "Id6",
Name = "Name6",
Path = "Path6"
}
};
var target = new List<MediaFolder>
{
new MediaFolder
{
Id = "Id1",
Name = "Actualizado en el objeto",
Path = "Path1"
},
//Id2 eliminado
new MediaFolder
{
Id = "Id3",
Name = "Name3",
Path = "Actualizado tambien"
},
new MediaFolder
{
Id = "Id4",
Name = "Name4",
Path = "Path4"
},
new MediaFolder
{
Id = "Id5",
Name = "Name5",
Path = "Path5"
},
new MediaFolder
{
Id = "Id6",
Name = "Name6",
Path = "Path6"
},
new MediaFolder
{
Id = "Id7",
Name = "Nuevo Item 7",
Path = "Nuevo Item 7"
}
};
var merge = source.Merge(target, (x, y) => x.Id == y.Id);
var toUpdate = merge.Matched((x, y) => x.Name != y.Name | x.Path != y.Path)
.ToArray();
var toDelete = merge.NotMatchedBySource();
var toInsert = merge.NotMatchedByTarget();
Assert.AreEqual(2, toUpdate.Count());
Assert.IsTrue(toUpdate.Count(x => x.Source.Id == "Id1" & x.Target.Id == "Id1") > 0);
Assert.IsTrue(toUpdate.Count(x => x.Source.Id == "Id3" & x.Target.Id == "Id3") > 0);
Assert.AreEqual("Id7", toInsert.First().Id);
Assert.AreEqual("Id2", toDelete.First().Id);
}
[TestMethod]
public void TestMerge2()
{
var source = new List<CustomObject>
{
new CustomObject
{
Year = 2010,
Month = 6,
Value = 2
},
new CustomObject
{
Year = 2010,
Month = 7,
Value = 5
},
new CustomObject
{
Year = 2010,
Month = 10,
Value = 3
}
};
var target = new List<CustomObject>
{
new CustomObject
{
Year = 2010,
Month = 7,
Value = 2
},
new CustomObject
{
Year = 2010,
Month = 8,
Value = 1
},
new CustomObject
{
Year = 2010,
Month = 10,
Value = 2
}
};
var merge = source.Merge(target, (x, y) => x.Year == y.Year && x.Month == y.Month);
var toUpdate = merge.Matched((x, y) => x.Value != y.Value)
.ToArray();
var inSourceButNotInTarget = merge.NotMatchedBySource();
var inTargetButNotInSource = merge.NotMatchedByTarget();
Console.WriteLine("Objects to Update");
foreach (var mergeMatched in toUpdate)
{
Console.WriteLine("{0} -{1} - {2} - {3}",
mergeMatched.Source.Year,
mergeMatched.Source.Month,
mergeMatched.Source.Value,
mergeMatched.Target.Value);
}
Console.WriteLine("In source but not in target");
foreach (var customObject in inSourceButNotInTarget)
{
Console.WriteLine("{0} -{1} - {2} - 0",
customObject.Year,
customObject.Month,
customObject.Value);
}
Console.WriteLine("In target but not in source");
foreach (var customObject in inTargetButNotInSource)
{
Console.WriteLine("{0} -{1} - 0 - {2}",
customObject.Year,
customObject.Month,
customObject.Value);
}
}
The following code is taken from the tutorial: http://www.asp.net/mvc/tutorials/getting-started-with-aspnet-mvc3/cs/examining-the-edit-methods-and-edit-view which shows how ASP.net MVC 3 can be used to manage a movie database.
In the tutoral, a list object is added to the controller class that contains every movie genre that exists in the database. This list is then passed to a drop-down in the view enabling the database to be searched by genre.
Controller: (code related to movie genre in bold)
public ActionResult SearchIndex(string movieGenre, string searchString)
{
var GenreLst = new List<string>();
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
GenreLst.AddRange(GenreQry.Distinct());
ViewBag.movieGenre = new SelectList(GenreLst);
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
if (string.IsNullOrEmpty(movieGenre))
return View(movies);
else
{
return View(movies.Where(x => x.Genre == movieGenre));
}
}
What I want to do is enhance this further so that the movies can be searched by price as well as genre. I know I can re-use the much of the same code to do this. I think I need to create a new class that the controller class can pass either the genre or price. Is this correct? IF so, I'd appreciate an example. Thanks.
Update/Clarification:
I want to avoid repeating the code for both genre and price as below:
public ActionResult SearchIndex(string movieGenre, string searchString,float moviePrice)
{
var GenreLst = new List<string>();
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
GenreLst.AddRange(GenreQry.Distinct());
ViewBag.movieGenre = new SelectList(GenreLst);
var PriceLst = new List<string>();
var PriceQry = from d in db.Movies
orderby d.Genre
select d.Genre;
PriceLst.AddRange(GenreQry.Distinct());
ViewBag.moviePrice = new SelectList(PriceLst);
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
if (string.IsNullOrEmpty(movieGenre))
return View(movies);
else
{
return View(movies.Where(x => x.Genre == movieGenre));
}
if (string.IsNullOrEmpty(moviePrice))
return View(movies);
else
{
return View(movies.Where(x => x.Genre == moviePrice));
}
}
You just have to insert a text box in the view to get price value. Then receive this value at action and modify the query to get desired results.
like this:
#Html.ActionLink("Create New", "Create")
#using (Html.BeginForm()){
<p>Genre: #Html.DropDownList("movieGenre", "All")
Title: #Html.TextBox("SearchString")
Price: #Html.TextBox("Price")
<input type="submit" value="Filter" /></p>
}
And in the action method you are using the code below to populate the dropdownlist with genre values. You need not do the same for price value.
var GenreLst = new List<string>();
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
GenreLst.AddRange(GenreQry.Distinct());
ViewBag.movieGenre = new SelectList(GenreLst);
And in your action method you just have to use the value of price to filter data
public ActionResult SearchIndex(string movieGenre, string searchString,float price)
{
var GenreLst = new List<string>();
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
GenreLst.AddRange(GenreQry.Distinct());
ViewBag.movieGenre = new SelectList(GenreLst);
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
if (string.IsNullOrEmpty(movieGenre))
return View(movies);
else
{
return View(movies.Where((x => x.Genre == movieGenre) &&(x => x.Price== price)));
}
}
You can do it in so many different ways while all are correct but it depends on the complexity of your project. Basically you don't want to over-engineer a simple program. But in general you should move all of your logic to a separate class and use your actions for creating and calling the right logic class:
public class GetMoviesRequest
{
public string Name { get; set; }
public float? Price { get; set; }
}
public class MoviesLogic
{
private List<Movie> Movies;
public IEnumerable<Movie> Get(GetMoviesRequest request)
{
IEnumerable<Movie> filtered = Movies.AsQueryable();
if (!string.IsNullOrEmpty(request.Name))
{
//Filter by name
filtered = filtered.Where(m => m.Name == request.Name);
}
if (request.Price.HasValue)
{
//Filter by value
filtered = filtered.Where(m => m.Price == request.Price);
}
return filtered;
}
}
public class MyController
{
public ActionResult SearchIndex(string movieGenre, string searchString)
{
var logic = new MoviesLogic();
var movies = logic.Get(new GetMoviesRequest() { Name = searchString } )
///do stuff with movies
}
}
Let say we have a class
Category
{
ID,
Name,
ParentID
}
and a List
1, 'Item 1', 0
2, 'Item 2', 0
3, 'Item 3', 0
4, 'Item 1.1', 1
5, 'Item 3.1', 3
6, 'Item 1.1.1', 4
7, 'Item 2.1', 2
Can we using LINQ to render a tree like:
Item 1
Item 1.1
Item 1.1.1
Item 2
Item 2.1
Item 3
Item 3.1
Any help is appreciated!
Here's the "LINQ-only" version:
Func<int, int, string[]> build = null;
build = (p, n) =>
{
return (from x in categories
where x.ParentID == p
from y in new[]
{
"".PadLeft(n)+ x.Name
}.Union(build(x.ID, n + 1))
select y).ToArray();
};
var lines = build(0, 0);
Yes, it's recursive LINQ.
Per NVA's request, here's the way to make all "orphan" records become root records:
Func<IEnumerable<int>, int, string[]> build = null;
build = (ps, n) =>
{
return (from x in categories
where ps.Contains(x.ParentID)
from y in new[]
{
"".PadLeft(n)+ x.Name
}.Union(build(new [] { x.ID }, n + 1))
select y).ToArray();
};
var roots = (from c in categories
join p in categories on c.ParentID equals p.ID into gps
where !gps.Any()
orderby c.ParentID
select c.ParentID).Distinct();
var lines = build(roots, 0);
These extension methods do exactly what you want:
public static partial class LinqExtensions
{
public class Node<T>
{
internal Node() { }
public int Level { get; internal set; }
public Node<T> Parent { get; internal set; }
public T Item { get; internal set; }
public IList<Node<T>> Children { get; internal set; }
}
public static IEnumerable<Node<T>> ByHierarchy<T>(
this IEnumerable<T> source,
Func<T, bool> startWith,
Func<T, T, bool> connectBy)
{
return source.ByHierarchy<T>(startWith, connectBy, null);
}
private static IEnumerable<Node<T>> ByHierarchy<T>(
this IEnumerable<T> source,
Func<T, bool> startWith,
Func<T, T, bool> connectBy,
Node<T> parent)
{
int level = (parent == null ? 0 : parent.Level + 1);
if (source == null)
throw new ArgumentNullException("source");
if (startWith == null)
throw new ArgumentNullException("startWith");
if (connectBy == null)
throw new ArgumentNullException("connectBy");
foreach (T value in from item in source
where startWith(item)
select item)
{
var children = new List<Node<T>>();
Node<T> newNode = new Node<T>
{
Level = level,
Parent = parent,
Item = value,
Children = children.AsReadOnly()
};
foreach (Node<T> subNode in source.ByHierarchy<T>(possibleSub => connectBy(value, possibleSub),
connectBy, newNode))
{
children.Add(subNode);
}
yield return newNode;
}
}
public static void DumpHierarchy<T>(this IEnumerable<Node<T>> nodes, Func<T, string> display)
{
DumpHierarchy<T>(nodes, display, 0);
}
private static void DumpHierarchy<T>(IEnumerable<LinqExtensions.Node<T>> nodes, Func<T, string> display, int level)
{
foreach (var node in nodes)
{
for (int i = 0; i < level; i++) Console.Write(" ");
Console.WriteLine (display(node.Item));
if (node.Children != null)
DumpHierarchy(node.Children, display, level + 1);
}
}
}
You can use them as follows:
categories.ByHierarchy(
cat => cat.ParentId == null, // assuming ParentId is Nullable<int>
(parent, child) => parent.Id == child.ParentId)
.DumpHierarchy(cat => cat.Name);
You can use recursion:
public class Category
{
public int ID { get; set; }
public string Name { get; set; }
public int ParentID { get; set; }
public List<Category> Children { get; set; }
}
class Program
{
static void Main()
{
List<Category> categories = new List<Category>()
{
new Category () { ID = 1, Name = "Item 1", ParentID = 0},
new Category() { ID = 2, Name = "Item 2", ParentID = 0 },
new Category() { ID = 3, Name = "Item 3", ParentID = 0 },
new Category() { ID = 4, Name = "Item 1.1", ParentID = 1 },
new Category() { ID = 5, Name = "Item 3.1", ParentID = 3 },
new Category() { ID = 6, Name = "Item 1.1.1", ParentID = 4 },
new Category() { ID = 7, Name = "Item 2.1", ParentID = 2 }
};
List<Category> hierarchy = new List<Category>();
hierarchy = categories
.Where(c => c.ParentID == 0)
.Select(c => new Category() { ID = c.ID, Name = c.Name, ParentID = c.ParentID, Children = GetChildren(categories, c.ID) })
.ToList();
HieararchyWalk(hierarchy);
Console.ReadLine();
}
public static List<Category> GetChildren(List<Category> categories, int parentId)
{
return categories
.Where(c => c.ParentID == parentId)
.Select(c => new Category { ID = c.ID, Name = c.Name, ParentID = c.ParentID, Children = GetChildren(categories, c.ID) })
.ToList();
}
public static void HieararchyWalk(List<Category> hierarchy)
{
if (hierarchy != null)
{
foreach (var item in hierarchy)
{
Console.WriteLine(string.Format("{0} {1}", item.ID, item.Name));
HieararchyWalk(item.Children);
}
}
}
}
public IEnumerable<HelpPageMenuItem> GetHelpPageMenuItems()
{
var helpPages = (from h in Context.HelpPages select new HelpPageMenuItem{HelpPageId = h.HelpPageId, ParentHelpPageId = h.ParentHelpPageId, PageContext = h.PageContext, MenuText = h.MenuText}).ToList();
var parents = from h in helpPages where !h.ParentHelpPageId.HasValue select PopulateChildren(h, helpPages);
return parents.ToList();
}
private static HelpPageMenuItem PopulateChildren(HelpPageMenuItem helpPageMenuItem, IEnumerable<HelpPageMenuItem> helpPages)
{
helpPageMenuItem.ChildHelpPages =
(from h in helpPages
where h.ParentHelpPageId == helpPageMenuItem.HelpPageId
select PopulateChildren(h, helpPages)).ToList();
return helpPageMenuItem;
}
#model List<OrgChart.Models.Node>
#{
Func<int?, List<OrgChart.Models.Node>, string> recuresive = null;
recuresive = (parentid, list) => string.Join("", list.Where(x => x.ParentId == parentid).Select(x => "<li>" + x.Name + "<ul>" + recuresive(x.Id, list.Where(y => y.ParentId != parentid).ToList()) + "</ul></li>"));
}
#Html.Raw("<ul id='org1' >" + recuresive(null, Model) + "</ul>")
<div id="chart" class="orgChart"></div>
public static List<TSource> BuildTreeView<TSource, TKey>(this List<TSource> allItems
, Func<TSource, TKey> parentSelector, Func<TSource, TKey> childSelector, Expression<Func<TSource, List<TSource>>> childrenPropertySelector
, Func<TSource, bool> GetRoot, List<TSource> rootList = null)
{
if (rootList == null)
rootList = allItems.Where(GetRoot).ToList();
if (rootList != null && rootList.Count > 0)
{
rootList.ForEach(rootItem =>
{
Func<TSource, bool> whereClause = x => childSelector(rootItem).Equals(parentSelector(x));
var childrenProperty = (childrenPropertySelector.Body as MemberExpression).Member as System.Reflection.PropertyInfo;
var childrenList = allItems.Where(whereClause).ToList();
childrenProperty.SetValue(rootItem, childrenList);
if (childrenList.Count > 0)
BuildTreeView(allItems, parentSelector, childSelector, childrenPropertySelector, GetRoot, childrenProperty.GetValue(rootItem) as List<TSource>);
});
}
return rootList;
}
//Call method
List<Channel> rootChannel = listChannel.BuildTreeView(f => f.PARENT_CODE, x => x.CODE, z => z.SubChannels, c => c.CODE == "AC");
how can i do the code
static string BuildMenu(List<Menu> menu, int? parentId)
{
foreach (var item in menu.Where(i => i.ParentMenu == parentId || i.ParentMenu.MenuId == parentId).ToList())
{
}
}
return BuildMenu(menuList,null);
so if parentId==null then return only records i => i.ParentMenu == null but when parentId is >0 then return records with i.ParentMenu.MenuId == parentId
You could do that in a single statement, probably something like this
var query = from menu in menus
where (parentId == null ? menu.ParentMenu == null : menu.ParentMenu != null && menu.ParentMenu.MenuId == parentId)
select menu;
But I think it would be more readable by breaking it up like the following
var query = menus.AsEnumerable();
if (parentId.HasValue)
query = query.Where(m => m.ParentMenu != null && m.ParentMenu.MenuId == parentId);
else
query = query.Where(m => m.ParentMenu == null);
Full example:
public class Menu
{
public Menu ParentMenu { get; set; }
public int MenuId { get; set; }
}
...
static void Main(string[] args)
{
List<Menu> menus = new List<Menu>();
for (int i = 0; i < 4; i++)
{
menus.Add(new Menu() { MenuId = i });
}
menus[2].ParentMenu = menus[0];
menus[3].ParentMenu = menus[1];
Console.WriteLine(BuildMenu(menus, 1));
Console.Read();
}
static string BuildMenu(List<Menu> menus, int? parentId)
{
var query = menus.AsEnumerable();
if (parentId.HasValue)
query = query.Where(m => m.ParentMenu != null && m.ParentMenu.MenuId == parentId);
else
query = query.Where(m => m.ParentMenu == null);
StringBuilder builder = new StringBuilder();
foreach (Menu menu in query)
{
// build your string
builder.Append(menu.MenuId + ";");
}
return builder.ToString();
}