Linq: group by with a non-unique key (preserving order) - linq

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;
}

Related

Accessing an extension method on a dbset subtype

I have an extension method defined as:
public static class CurrentItemExtensions
{
static GPOPricingEntities ctx = new GPOPricingEntities();
public static List<CurrentItem> Get(this DbSet<CurrentItem> item, int tierId, string contractId)
{
List<CurrentItem> items = ctx.Items.OfType<CurrentItem>().Where(x => x.TierId == tierId).ToList();
if (items == null)
{
GPOPricing.AS400Models.ItemCollection collection = new GPOPricing.AS400Models.ItemCollection().Get(contractId);
foreach (var c in collection)
{
CurrentItem target = new CurrentItem();
target.Price = c.DirectPriceEaches;
target.SKU = c.LongItemNbr;
target.Description = c.Description;
target.ProductLine = c.ProductLine;
items.Add(target);
}
}
else
{
foreach (var i in items)
{
GPOPricing.AS400Models.Item as400Item = new GPOPricing.AS400Models.ItemCollection().GetBySKU(i.SKU);
i.Description = as400Item.Description;
i.ProductLine = as400Item.ProductLine;
}
}
return items;
}
}
The problem I'm having is accessing it - CurrentItem is a subtype of Item. So I've tried:
db.Items.Get (doesn't work)
and I have tried
db.Items.OfType<CurrentItem>().Get (doesn't work)
Any suggestions?
I found that I had to use the base type and create a method for each subtype:
public static class CurrentItemExtensions
{
static GPOPricingEntities ctx = new GPOPricingEntities();
public static List<CurrentItem> GetCurrentItems(this DbSet<Item> item, int tierId, string contractId)
{
List<CurrentItem> items = ctx.Items.OfType<CurrentItem>().Where(x => x.TierId == tierId).ToList();
if (items.Count() == 0)
{
GPOPricing.AS400Models.ItemCollection collection = new GPOPricing.AS400Models.ItemCollection().Get(contractId);
foreach (var c in collection)
{
CurrentItem target = new CurrentItem();
target.Price = c.DirectPriceEaches;
target.SKU = c.LongItemNbr;
items.Add(target);
}
}
else
{
foreach (var i in items)
{
GPOPricing.AS400Models.Item as400Item = new GPOPricing.AS400Models.ItemCollection().GetBySKU(i.SKU);
}
}
return items;
}
}

Combine two records into one and update column

I have the following records in my CartItems table:
When I migrate the cart, I'd like to combine the two records into one with the cart id of Test1 and then update the quantity to 2.
So it would be CartItemId 84, CartId Test1, Quantity 2, DateCreated 5/21/2013, ProductId 16
How would I do this in linq/EF?
EDIT
public void MigrateCart(string userName)
{
ShoppingCartId = GetCartId();
var shoppingCart = _context.ShoppingCartItems.Where(c => c.CartId == ShoppingCartId);
foreach (var item in shoppingCart)
{
item.CartId = userName;
}
_context.SaveChanges();
}
EDIT
I think I have it.
public void MigrateCart(string userName)
{
ShoppingCartId = GetCartId();
if (ShoppingCartId != userName)
{
var shoppingCart = _context.ShoppingCartItems.Where(c => c.CartId == ShoppingCartId).ToList();
var userShoppingCartItems = _context.ShoppingCartItems.Where(c => c.CartId == userName).ToList();
foreach (var item in shoppingCart)
{
foreach (var userShoppingCartItem in userShoppingCartItems)
{
if (item.ProductId == userShoppingCartItem.ProductId)
{
item.Quantity += userShoppingCartItem.Quantity;
item.CartId = userName;
_context.ShoppingCartItems.Remove(userShoppingCartItem);
}
}
item.CartId = userName;
}
_context.SaveChanges();
}
else
{
var shoppingCart = _context.ShoppingCartItems.Where(c => c.CartId == ShoppingCartId);
foreach (var item in shoppingCart)
{
item.CartId = userName;
}
_context.SaveChanges();
}
}
as you don't ask how to get the two items, I just give you a really rustic way.
var item1 = context.CartItems.FirstOrDefault(m => m.Id == 84);
var item2 = context.CartItems.FirstOrDefault(m => m.Id == 85);
item1.Quantity += item2.Quantity;//add quantity of item2 to item1
context.CartItems.Remove(item2);//remove item2
context.SaveChanges();

How can I merge or combine two records into one using Linq?

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);
}
}

Rendering a hierarchy using LINQ?

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");

linq null parameter

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();
}

Resources