Remove inner list element based on condition using lambda expression - c#

Remove inner list element based on condition using lambda expression. I have achieved it through foreach. Need to implement using lambda expression or Linq
Please help
if (!FwContext.User.UserRoles.Exists(s => s.RoleCd == "Admin"))
{
foreach (var item in items.ToList())
{
foreach (var attribute in item.ItemAttributeValues.ToList())
{
if (attribute.AttributeNm == "EST" && attribute.StatusNm == "SAP")
{
item.ItemAttributeValues.Remove(attribute);
}
}
}
}

Like this?
using System;
using System.Collections.Generic;
using System.Linq;
namespace RemoveItemFromListWithLinq_42565975
{
class Program
{
static void Main(string[] args)
{
DoIt();
}
private static void DoIt()
{
List<string> thelist = new List<string>() { "file1", "file2", "file3" };
thelist = thelist.Where(p => p != "file2").ToList();
Console.WriteLine(string.Join(",", thelist));
Console.ReadLine();
}
}
}
Using attributes like your opening post
using System;
using System.Collections.Generic;
using System.Linq;
namespace RemoveItemFromListWithLinq_42565975
{
class Program
{
static void Main(string[] args)
{
DoIt();
}
private static void DoIt()
{
List<string> thelist = new List<string>() { "file1", "file2", "file3" };
thelist = thelist.Where(p => p.Length == 5 && p != "file2" ).ToList();
Console.WriteLine(string.Join(",", thelist));
Console.ReadLine();
}
}
}

Currently what I found is below. And it is working fine.
items.ToList().ForEach(item =>
{
var attr = item.ItemAttributeValues.ToList().Where(x => x.AttributeNm == "EST" && x.StatusNm == "SAP").FirstOrDefault();
item.ItemAttributeValues.Remove(attr);
}
);
please tell if we can do it in better way.

items.ToList().ForEach(item => item.ItemAttributeValues.Remove(item.ItemAttributeValues.ToList().ToList().Where(x => x.AttributeNm == "EST_DIE_SZ_AREA" && x.StatusNm == "SAP Adopted").FirstOrDefault()));
Gave the query a small modification

Related

How to merge two custom types list in C#

I have two List, How can i merge or combine them.
first list similar to this one :
dateList = [{01/10/2018, 02/10/2018, 03/10/2018, 04/10/2018, 05/10/2018, 06/10/2018}]
second List similar to this one :
markStatus = [{01/10/2018, true}, {03/10/2018, true}, {05/10/2018, false }]
I want the result to be like :
result = `[{01/10/2018, true}, {2/10/2018, false}, {03/10/2018, true}, {04/10/2018, false}, {05/10/2018, false}, {06/10/2018, false}]
Having to guess at some property names here:
dateList.Select(d => (d, markStatus.FirstOrDefault(s => s.Date == d)?.Select(s => s.Status) ?? false));
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp16
{
class Program
{
static void Main(string[] args)
{
string[] dateList = { "01/10/2018", "02/10/2018", "03/10/2018", "04/10/2018", "05/10/2018", "06/10/2018" };
Dictionary<string, bool> markStatus = new Dictionary<string, bool>()
{
{"01/10/2018",true } ,
{"03/10/2018",true },
{"05/10/2018",false }
};
Dictionary<string, bool> result = new Dictionary<string, bool>();
bool flag;
foreach (var item in dateList)
{
flag = false;
foreach (var key in markStatus.Keys)
{
if (item.Equals(key))
{
result.Add(item, markStatus[key]);
flag = true;
break;
}
}
if (!flag)
{
result.Add(item, false);
}
}
}
}
}

Linq group data in a flattened enumerable

Period|1|
AA|0|0|32.39|0|0|-0.12|
BB|0|-1794.62|
CC|Entity1|25|31.48|244.1|
DD|Entity2|25|0|0|
Period|2|
AA|0|0|32.39|0|0|-0.12|
BB|0|-1794.62|
CC|Entity1|25|31.48|244.1|
EE|Entity2|25|0|0|
FF|Entity3|25|0|0|
GG|Entity4|25|0|0|
HH|Entity5|25|0|0|
Period|3|
AA|0|0|32.39|0|0|-0.12|
BB|0|-1794.62|
Consider the above collection as:
IEnumerable<IEnumerable<string>> data;
First Enumerable is each line.
Second Enumerable is each line separated by delimiter |
I would like to group this by each period:
Expected result:
Period1 (Group Key)
AA|0|0|32.39|0|0|-0.12|
BB|0|-1794.62|
CC|Entity1|25|31.48|244.1|
DD|Entity2|25|0|0|
Period2 (Group Key)
AA|0|0|32.39|0|0|-0.12|
BB|0|-1794.62|
CC|Entity1|25|31.48|244.1|
EE|Entity2|25|0|0|
FF|Entity3|25|0|0|
GG|Entity4|25|0|0|
HH|Entity5|25|0|0|
Period3 (Group Key)
AA|0|0|32.39|0|0|-0.12|
BB|0|-1794.62|
Current implementation:
foreach (var dataPerPeriod in data.Take(5))
{
yield return new DataPerPeriod(dataPerPeriod);
}
but as you can see only the first period has 5 elements including the would-be key element (period).
Therefore I do not understand how to approach this problem.
I have made help class for your DataPerPeriod:
public class DataPerPeriod
{
public string Name { get; set; }
public List<IEnumerable<string>> Lines { get; set;}
}
Than i could aggregate it with this query:
var res = data.Aggregate(new List<DataPerPeriod>(), (a, b) =>
{
if (b.First() =="Period")
{
a.Add(new DataPerPeriod { Name = String.Join("", b),
Lines = new List<IEnumerable<string>>() });
}
else
{
a.Last().Lines.Add(b);
}
return a;
});
Result is:
Not pure LINQ, but with the help of the little "LINQ spirit" custom generic extension method which allows you to split (partition) a sequence based on condition:
public static class LinqExtensions
{
public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, Func<T, bool> splitOn)
{
using (var e = source.GetEnumerator())
{
for (bool more = e.MoveNext(); more;)
{
var group = new List<T> { e.Current };
while ((more = e.MoveNext()) && !splitOn(e.Current))
group.Add(e.Current);
yield return group;
}
}
}
}
the concrete issue can easily be solved with something like this:
IEnumerable<IEnumerable<string>> source = ...;
var result = source
.Split(e => e.FirstOrDefault() == "Period")
.Select(g => new
{
Key = g.First().Skip(1).FirstOrDefault(),
Elements = g.Skip(1)
});
Not particularly elegant (but then, neither is your dataset) but this works:
public static Dictionary<IEnumerable<string>,IEnumerable<IEnumerable<string>>> Parse(IEnumerable<IEnumerable<string>> input)
{
IEnumerable<string> key = null;
var rows = new List<IEnumerable<string>>();
var result = new Dictionary<IEnumerable<string>,IEnumerable<IEnumerable<string>>>();
foreach(var row in input)
{
if(row.First().StartsWith("Period"))
{
if(key != null)
result.Add(key,rows.AsEnumerable());
key = row;
rows = new List<IEnumerable<string>>();
}
else
{
rows.Add(row);
}
}
result.Add(key,rows);
return result;
}
Live example: http://rextester.com/ZMUM90524
I've been parsing text files for 40 years. If I can't do it nobody can. My solution is almost the same as Jamiec just a little different in style
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication43
{
class Program
{
const string FILENAME = #"c:\temp\test.txt";
static void Main(string[] args)
{
StreamReader reader = new StreamReader(FILENAME);
string inputLine = "";
Dictionary<string, List<string>> data = new Dictionary<string, List<string>>();
List<string> period = null;
while ((inputLine = reader.ReadLine()) != null)
{
inputLine = inputLine.Trim();
if (inputLine.Length > 0)
{
if (inputLine.StartsWith("Period"))
{
string key = inputLine.Replace("|", "");
period = new List<string>();
data.Add(key, period);
}
else
{
period.Add(inputLine);
}
}
}
}
}
}

Lazy combinations

I'm looking for a more lazy/IEnumerable/cleaner way of doing the following. I particularly not happy with the use of helper and Aggregate.
Any hints on how to modify the code to make it possible?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Test1
{
class Program
{
static void PrintOut<T>(IEnumerable<IEnumerable<T>> data)
{
foreach (var item in data)
{
string output = "-";
if (item != null)
output = string.Join(",", item.Select(x => (x == null) ? "-" : x.ToString()));
Console.WriteLine(output);
}
}
static IEnumerable<T> helper<T>(IEnumerable<T> orig, T toAdd)
{
if (orig != null)
foreach (var item in orig)
yield return item;
yield return toAdd;
yield break;
}
static IEnumerable<IEnumerable<T>> helper2<T>(IEnumerable<IEnumerable<T>> input) where T : class
{
var initalAcc = new List<IEnumerable<T>> { };
var result = input.Aggregate(initalAcc,
(acc, choiceSet) =>
acc.DefaultIfEmpty()
.SelectMany((chosen) => (choiceSet ?? new List<T> { }).DefaultIfEmpty().Select(choice => helper(chosen, choice))).ToList()
);
return result;
}
static void Main(string[] args)
{
var preCombination = new List<List<string>> {
new List<string> {"1","2"},
new List<string> {"3"},
new List<string> {"4","5"},
null,
new List<string> {"6","7"}
};
var postCombination = helper2(preCombination);
PrintOut(preCombination);
Console.WriteLine();
PrintOut(postCombination);
Console.ReadLine();
}
}
}
Here is the expected output
1,2
3
4,5
-
6,7
1,3,4,-,6
1,3,4,-,7
1,3,5,-,6
1,3,5,-,7
2,3,4,-,6
2,3,4,-,7
2,3,5,-,6
2,3,5,-,7
I've changed initalAcc now
var initalAcc = Enumerable.Empty<IEnumerable<T>>();
Here you go. ConcatItem and Yield replace helper.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Test1
{
class Program
{
static void PrintOut<T>(IEnumerable<IEnumerable<T>> data)
{
foreach (var item in data)
{
string output = "-";
if (item != null)
output = string.Join(",", item.Select(x => (x == null) ? "-" : x.ToString()));
Console.WriteLine(output);
}
}
static IEnumerable<T> Yield<T>(T item)
{
yield return item;
}
static IEnumerable<T> ConcatItem<T>(IEnumerable<T> enumerable, T item)
{
return enumerable == null ? Yield(item) : enumerable.Concat(Yield(item));
}
static IEnumerable<IEnumerable<T>> helper2<T>(IEnumerable<IEnumerable<T>> input) where T : class
{
var initalAcc = Enumerable.Empty<IEnumerable<T>>();
var result = input.Aggregate(initalAcc,
(acc, choiceSet) =>
acc.DefaultIfEmpty()
.SelectMany((chosen) => (choiceSet ?? Enumerable.Empty<T>()).DefaultIfEmpty().Select(choice => ConcatItem(chosen, choice)))
);
return result;
}
static void Main(string[] args)
{
var preCombination = new List<List<string>> {
new List<string> {"1","2"},
new List<string> {"3"},
new List<string> {"4","5"},
null,
new List<string> {"6","7"},
};
var postCombination = helper2(preCombination);
PrintOut(preCombination);
Console.WriteLine();
PrintOut(postCombination);
Console.ReadLine();
}
}
}

c# Lambda Expression built with LinqKit does not compile

This lambda does not compile, but I do not understand why.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using LinqKit;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var barModel = new BarModel();
string id = "some";
Console.WriteLine(barModel.subFor(id).ToString());
// output: m => (True AndAlso (m.key == value(ConsoleApplication2.Bar`1+<>c__DisplayClass0[ConsoleApplication2.Model]).id))
Console.ReadKey();
var subworkitems = barModel.list.Where(barModel.subFor(id).Compile());
// Exception {"variable 'm' of type 'ConsoleApplication2.Model' referenced from scope '', but it is not defined"}
Console.WriteLine(subworkitems.ToString());
Console.ReadKey();
}
}
class Bar<TModel>
{
public Bar(Expression<Func<TModel, string>> foreignKeyExpression)
{
_foreignKeyExpression = foreignKeyExpression;
}
private Expression<Func<TModel, string>> _foreignKeyExpression { get; set; }
public Expression<Func<TModel, bool>> subFor(string id)
{
var ex = forTargetId(id);
return ex;
}
public Expression<Func<TModel, bool>> forTargetId(String id)
{
var fc = _foreignKeyExpression;
Expression<Func<TModel, bool>> predicate = m => true;
var result = predicate.And(m => fc.Invoke(m) == id).Expand();
return result;
}
}
class Model
{
public string key;
public string value;
}
class BarModel : Bar<Model>
{
public List<Model> list;
public BarModel() : base(m => m.key)
{
list = new List<Model>();
}
}
}
Solution 1
This solution doesn't strip out the invoke statements which I assume you are trying to do by calling "Expand".
change the result of "forTargetId(String id)" to
predicate.And(m => fc.Invoke(m) == id);
When the expression is compiled in the where clause it will know that it needs to pass "m" to the fc expression above.
My first tip came when I changed
predicate.And(m => fc.Invoke(m) == id).Expand();
to
predicate.And(n => fc.Invoke(n) == id).Expand();
and I could see that n was not being pass along at all.
I tested out this change by manipulating the Main method as follows
static void Main(string[] args)
{
var barModel = new BarModel();
barModel.list.Add(new Model() { key = "1", value = "One" });
barModel.list.Add(new Model() { key = "2", value = "Two" });
barModel.list.Add(new Model() { key = "some", value = "Three" });
string id = "some";
Console.WriteLine(barModel.subFor(id).ToString());
// output: m => (True AndAlso (m.key == value(ConsoleApplication2.Bar`1+<>c__DisplayClass0[ConsoleApplication2.Model]).id))
Console.ReadKey();
var subworkitems = barModel.list.Where(barModel.subFor(id).Compile());
// Exception {"variable 'm' of type 'ConsoleApplication2.Model' referenced from scope '', but it is not defined"}
foreach (var si in subworkitems)
{
Console.WriteLine(si.key);
Console.WriteLine(si.value);
}
Console.WriteLine(subworkitems.ToString());
Console.ReadKey();
}
Solution 2
This solution does remove the Invoke statements with the use of the Expand method but changes the way that you are anding the statements together.
The "Expand" function is advertised as following on the LinqKit website.
Expression<Func<Purchase, bool>> criteria1 = p => p.Price > 1000;
Expression<Func<Purchase, bool>> criteria2 = p => criteria1.Invoke(p) || p.Description.Contains("a");
Console.WriteLine(criteria2.Expand().ToString());
// p => ((p.Price > 1000) || p.Description.Contains("a"))
Notice that they aren't using the "And" method to put these things together but instead they are "daisy chaining" the calls together.
Instead of
Expression<Func<TModel, bool>> predicate = m => true;
var result = predicate.And(m => fc.Invoke(m) == id).Expand();
Do this
Expression<Func<TModel, bool>> predicate = m => fc.Invoke(m) == id && true;
return predicate.Expand();

Error when using constructor for a generic list that takes a collection

I was trying out parsing XML using Fluent interface as described here.
The example is good but gives one compiler error that I am not able to fix.
The error is in the protected constructor below:
protected DynamicXml( IEnumerable<XElement> elements )
{
_elements = new List<XElement>( elements ); // error on this line
}
The two compiler errors I get on this line are:
Argument 1: cannot convert from
'System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>' to
'System.Xml.Linq.XElement'
and
The best overloaded Add method
'System.Collections.Generic.List<System.Xml.Linq.XElement>.
Add(System.Xml.Linq.XElement)' for the collection initializer
has some invalid arguments
I have copy/pasted the full code below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Dynamic;
using System.Xml.Linq;
using System.Collections;
namespace FluentXML
{
public class DynamicXml : DynamicObject, IEnumerable
{
private readonly List<XElement> _elements;
public DynamicXml(string text)
{
var doc = XDocument.Parse(text);
_elements = new List<XElement> { doc.Root };
}
protected DynamicXml(XElement element)
{
_elements = new List<XElement> { element };
}
protected DynamicXml(IEnumerable<XElement> iteratorElem)
{
_elements = new List<XElement> { iteratorElem };
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
if (binder.Name == "Value")
result = _elements[0].Value;
else if (binder.Name == "Count")
result = _elements.Count;
else
{
var attr = _elements[0].Attribute(
XName.Get(binder.Name));
if (attr != null)
result = attr;
else
{
var items = _elements.Descendants(
XName.Get(binder.Name));
if (items == null || items.Count() == 0)
return false;
result = new DynamicXml(items);
}
}
return true;
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
int ndx = (int)indexes[0];
result = new DynamicXml(_elements[ndx]);
return true;
}
public IEnumerator GetEnumerator()
{
foreach (var element in _elements)
yield return new DynamicXml(element);
}
}
}
namespace FluentXML
{
class Program
{
static void Main(string[] args)
{
string xmlString = FluentXML.Properties.Resources.InputString;
dynamic dx = new DynamicXml(xmlString);
Console.WriteLine("PublicationDate='{0}'", dx.pubdate.Value);
Console.WriteLine("BookCount='{0}'", dx.book.Count);
foreach (dynamic d in dx.book)
{
Console.WriteLine("----- Begin Book -----");
Console.WriteLine("Price='{0}'", d.price.Value);
Console.WriteLine("Title='{0}'", d.title.Value);
Console.WriteLine("AuthorCount='{0}'", d.authors.author.Count);
foreach (dynamic a in d.authors.author)
{
Console.WriteLine(" ---- Begin Author ----");
Console.WriteLine(" EmailAddress='{0}'", a.email.address.Value);
Console.WriteLine(" FirstName='{0}'", a.name.first.Value);
Console.WriteLine(" MiddleName='{0}'", a.name.middle.Value);
Console.WriteLine(" LastName='{0}'", a.name.last.Value);
Console.WriteLine(" ----- End Author -----");
}
Console.WriteLine("------ End Book ------");
}
}
}
}
protected DynamicXml(IEnumerable<XElement> iteratorElem)
{
_elements = new List<XElement> { iteratorElem };
}
is the sequence initializer. This would create a list with one element "iteratorElem" which isn't of type XElement (as the error says).
Calling:
protected DynamicXml(IEnumerable<XElement> iteratorElem)
{
_elements = new List<XElement>(iteratorElem);
}
should work, though. You could also write iteratorElem.ToList()
The protected constructor that you posted in the first code sample is not the same as the one you posted in the second code sample...
This one will work!
protected DynamicXml( IEnumerable<XElement> elements )
{
_elements = new List<XElement>( elements ); // error on this line
}
This one will not! :(
protected DynamicXml(IEnumerable<XElement> iteratorElem)
{
_elements = new List<XElement> { iteratorElem };
}
The difference is the use of parentheses vs an initializer block (not sure if that is the technical term). The parenthesized version will call the generic List constructor overload that takes a generic IEnumerable as a parameter. The version with the braces will attempt to add the generic IEnumerable as a single element in the list.

Categories