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();
}
}
}
Related
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
This is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication2
{
public class c_Thread
{
public bool ThreadOngoing;
public c_Thread()
{
ThreadOngoing = false;
}
public void CallToChildThread(string key, ref List<int> nums,
ref Dictionary<string, List<int>> container)
{
container.Add(key, nums);
}
}
class Program
{
static void Main(string[] args)
{
c_Thread cc = new c_Thread();
Dictionary<string, List<int>> container1 = new Dictionary<string, List<int>>();
List<int> aList = new List<int>();
List<int> bList = new List<int>();
string printLine;
aList.Add(2);
aList.Add(4);
aList.Add(2);
bList.Add(1);
bList.Add(3);
bList.Add(1);
Thread myNewThread1 = new Thread(() =>
cc.CallToChildThread("param1", ref aList, ref container1));
myNewThread1.Start();
Thread myNewThread2 = new Thread(() =>
cc.CallToChildThread("param2", ref bList, ref container1));
myNewThread2.Start();
while (myNewThread1.ThreadState == ThreadState.Running &&
myNewThread2.ThreadState == ThreadState.Running)
{ }
foreach (string key in container1.Keys)
{
printLine = key + ": ";
foreach(int val in container1[key])
printLine += val + " ";
Console.WriteLine(printLine);
}
Console.ReadKey();
}
}
}
I am trying to add items in parallel into a dictionary however, the following is printed out:
Ideally I want to process a txt file with multiple columns and I want to input all into a dictionary, however, it takes a lot of time.
param1: 2 4 2
the second is not printed out, how can I rectify this?
The issue is Dictionary is not thread safe. You want to use a ConcurrentDictionary.
public class c_Thread
{
public bool ThreadOngoing;
public c_Thread()
{
ThreadOngoing = false;
}
public void CallToChildThread(string key, ref List<int> nums, ConcurrentDictionary<string, List<int>> container)
{
container.TryAdd(key, nums);
}
}
class Program
{
static void Main(string[] args)
{
var cc = new c_Thread();
var container1 = new ConcurrentDictionary<string, List<int>>();
var aList = new List<int>();
var bList = new List<int>();
string printLine;
aList.Add(2);
aList.Add(4);
aList.Add(2);
bList.Add(1);
bList.Add(3);
bList.Add(1);
var myNewThread1 = new Thread(() => cc.CallToChildThread("param1", ref aList, container1));
myNewThread1.Start();
var myNewThread2 = new Thread(() => cc.CallToChildThread("param2", ref bList, container1));
myNewThread2.Start();
while (myNewThread1.ThreadState == ThreadState.Running && myNewThread2.ThreadState == ThreadState.Running) { }
foreach (var key in container1.Keys)
{
printLine = key + ": ";
foreach (var val in container1[key]) printLine += val + " ";
Console.WriteLine(printLine);
}
Console.ReadKey();
}
}
Please have a look at Parallel.ForEach
Ideally you should use ConcurrentDictionary though.
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);
}
}
}
}
}
}
With the use of delegates, I want number 5 from IEnumerable items to print to the screen by using the following code;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using extended;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
IEnumerable<int> cities = new[] { 1, 2, 3, 4, 5 };
IEnumerable<int> query = cities.StartsWith(hello);
foreach (var item in query)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
static int hello(int x)
{
return x > 4 ? x : 0;
}
}
}
namespace extended
{
public static class A
{
public static IEnumerable<T> StartsWith<T>(this IEnumerable<T> input, inputdelegate<T> predicate)
{
foreach (var item in input)
{
if (item.Equals(predicate))
{
yield return item;
}
}
}
public delegate int inputdelegate<T>(T input);
}
}
code compiles without any error but displays no output to the screen. Any idea where I might be going wrong?
You are not invoking your predicate. Also, inputdelegate should probably have a return type of T. Change your code to this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using extended;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
IEnumerable<int> cities = new[] { 1, 2, 3, 4, 5 };
IEnumerable<int> query = cities.StartsWith(hello);
foreach (var item in query)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
static int hello(int x)
{
return x > 4 ? x : 0;
}
}
}
namespace extended
{
public static class A
{
public static IEnumerable<T> StartsWith<T>(this IEnumerable<T> input, inputdelegate<T> predicate)
{
foreach (var item in input)
{
if (item.Equals(predicate(item)))
{
yield return item;
}
}
}
public delegate T inputdelegate<T>(T input);
}
}
UPDATE: Based on comments from AlexD, you should consider changing your test to:
if (predicate(item))
and updating your delegate to:
public delegate bool inputdelegate<T>(T input);
and updating your Hello function to:
static bool hello(int x)
{
return x > 4;
}
How can this method be changed to act as a NotInRange? It should return only the items where the predicate does not match of the the supplied values.
Update
The method name, InRange, is a bit misleading and should probably be WhereInRange (or similar), since it does not return a Boolean; NotInRange should then be WhereNotInRange.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace ConsoleApplication5 {
/// SAMPLE USAGE
class Program {
static void Main(string[] args) {
// get some ids to play with...
string[] ids;
using(var ctx = new DataClasses1DataContext()) {
ids = ctx.Customers.Select(x => x.CustomerID)
.Take(100).ToArray();
}
// now do our fun select - using a deliberately small
// batch size to prove it...
using (var ctx = new DataClasses1DataContext()) {
ctx.Log = Console.Out;
foreach(var cust in ctx.Customers
.InRange(x => x.CustomerID, 5, ids)) {
Console.WriteLine(cust.CompanyName);
}
}
}
}
/// THIS IS THE INTERESTING BIT
public static class QueryableChunked {
public static IEnumerable<T> InRange<T, TValue>(
this IQueryable<T> source,
Expression<Func<T, TValue>> selector,
int blockSize,
IEnumerable<TValue> values) {
MethodInfo method = null;
foreach(MethodInfo tmp in typeof(Enumerable).GetMethods(
BindingFlags.Public | BindingFlags.Static)) {
if(tmp.Name == "Contains" && tmp.IsGenericMethodDefinition
&& tmp.GetParameters().Length == 2) {
method = tmp.MakeGenericMethod(typeof (TValue));
break;
}
}
if(method==null) throw new InvalidOperationException(
"Unable to locate Contains");
foreach(TValue[] block in values.GetBlocks(blockSize)) {
var row = Expression.Parameter(typeof (T), "row");
var member = Expression.Invoke(selector, row);
var keys = Expression.Constant(block, typeof (TValue[]));
var predicate = Expression.Call(method, keys, member);
var lambda = Expression.Lambda<Func<T,bool>>(
predicate, row);
foreach(T record in source.Where(lambda)) {
yield return record;
}
}
}
public static IEnumerable<T[]> GetBlocks<T>(
this IEnumerable<T> source, int blockSize) {
List<T> list = new List<T>(blockSize);
foreach(T item in source) {
list.Add(item);
if(list.Count == blockSize) {
yield return list.ToArray();
list.Clear();
}
}
if(list.Count > 0) {
yield return list.ToArray();
}
}
}
}
This method has been copied from Marc Gravell's answer to this question.
I think you need to add this to the predicate
var predicate = Expression.Not(Expression.Call(method, keys, member));
Not sure if this is a possible solution but how about something like:
public static IEnumerable<T> NotInRange<T,TValue)(this IQueryable<T> source, Expression<Func<T,TValue>> selector, int blockSize, IEnumerable<TValue> values)
{
return !source.InRange(selector, blockSize, values);
}