can i search in list with Dynamic LINQ parser - c#

in code page https://www.codeproject.com/Articles/355513/Invent-your-own-Dynamic-LINQ-parser
first read above page
i want search in list in list
var item2 = new List<List<object>>()
{
new List<object>{"a", 1000 },
new List<object>{"n", 900, 1000},
};
string s = "[0] == \"a\" ";
but dont work
please help me
my variable is not constant
condition is dynamic and create from end user and maybe have (&& || == >= != and .....)
var pred = SimpleExpression.PredicateParser<Element>.Parse(s);
this line error in our code

You can do something like this
var item2 = new List<List<object>>()
{
new List<object>{"a", 1000 },
new List<object>{"n", 900 ,1000},
};
foreach(List<object> list in item2)
{
foreach(object obj in list)
{
if(obj == "a")
{
Console.WriteLine("find!");
}
}
}

Not direct answer to your quetion, but you can use well known Dynamic Linq
var item2 = new List<List<object>>()
{
new List<object>{"a", 1000 },
new List<object>{"n", 900, 1000},
};
string s = "x => x[0] == \"a\" ";
var result = item2.AsQueryable().Where(s).ToList();
You can try it in dotnetfiddle

Related

IEnumerable.Select() when attribute is known only at runtime

Say I have a data class like this and a list of its objects:
public class DataSet
{
public int A { get; set; }
public string B { get; set; }
public double C { get; set; }
}
var data = new List<DataSet>
{
new DataSet() { A = 1, B = "One", C = 1.1 },
new DataSet() { A = 2, B = "Two", C = 2.2 },
new DataSet() { A = 3, B = "Three", C = 3.3 }
};
I would like to do a Select() on the list, based on different properties. For example, if I need a list of property A, I could do this easily:
var listA = data.Select(x => x.A).ToList();
All good so far.
But in my program, I need to do the above, only, I wouldn't know whether I need a list of A or B or C until runtime. This 'knowledge' of what to select is stored in a list of strings, and I need to iterate it and extract only the appropriate lists. Something like this:
// GetKeys() will return the keys that I need to extract.
// So at one time keyList could have "A" and "B", another time "B" and "C" etc.
List<string> keyList = GetKeys();
foreach (var key in keyList)
{
// What do I do here?
data.Select(x =>???).ToList();
}
Is this possible at all? I'm fine with even a non-LINQ solution, if it achieves my goal.
EDIT:
Clarifying the requirement.
The end result I want is a separate list based on each 'key' mentioned above. So, something like
List<List<object>>
The count in outer list would be the count of keyList.
The inner list would have as many items as in DataSet.
This would probably not be the most efficient solution, but you could use Reflection for a fully dynamic solution:
private static List<List<object>> SelectDynamicData<T>(IEnumerable<T> data, List<string> properties)
{
// get the properties only once per call
// this isn't fast
var wantedProperties = typeof(T)
.GetProperties()
.Where(x => properties.Contains(x.Name))
.ToArray();
var result = new Dictionary<string, List<object>>();
foreach (var item in data)
{
foreach (var wantedProperty in wantedProperties)
{
if (!result.ContainsKey(wantedProperty.Name))
{
result.Add(wantedProperty.Name, new List<object>());
}
result[wantedProperty.Name].Add(wantedProperty.GetValue(item));
}
}
return result.Select(x => x.Value).ToList();
}
And, of course, you'd need to do a double foreach or a LINQ query to print that. For example:
var data = new List<DataSet>
{
new DataSet() { A = 1, B = "One", C = 1.1 },
new DataSet() { A = 2, B = "Two", C = 2.2 },
new DataSet() { A = 3, B = "Three", C = 3.3 }
};
var selectedData = SelectDynamicData(data, new List<string> { "A", "C" });
foreach (var list in selectedData)
{
foreach (object item in list)
{
Console.Write(item + ", ");
}
Console.WriteLine();
}
Using Creating Expression Trees by Using the API you can build an expression tree to represent the linq query you were hard coding in order to make it more dynamic.
Expression<Func<TModel, object>> GetPropertyExpression<TModel>(string propertyName) {
// Manually build the expression tree for
// the lambda expression v => v.PropertyName.
// (TModel v) =>
var parameter = Expression.Parameter(typeof(TModel), "v");
// (TModel v) => v.PropertyName
var property = Expression.Property(parameter, propertyName);
// (TModel v) => (object) v.PropertyName
var cast = Expression.Convert(property, typeof(object));
var expression = Expression.Lambda<Func<TModel, object>>(cast, parameter);
return expression;
}
Review the comments to understand the building of the expression tree.
This now can be used with the data to extract the desired result.
Following similar to what was provided in another answer it would be simplified to
List<List<object>> SelectDynamicData<T>(IEnumerable<T> data, List<string> properties) {
return properties
.Select(_ => data.Select(GetPropertyExpression<T>(_).Compile()).ToList())
.ToList();
}
Both methods are displayed in the following example
[TestMethod]
public void TestMethod1() {
var data = new List<DataSet>
{
new DataSet() { A = 1, B = "One", C = 1.1 },
new DataSet() { A = 2, B = "Two", C = 2.2 },
new DataSet() { A = 3, B = "Three", C = 3.3 }
};
var propertyKnownAtRuntime = "A";
var expression = GetPropertyExpression<DataSet>(propertyKnownAtRuntime);
var listA = data.Select(expression.Compile()).ToList();
//Produces
// { 1, 2, 3}
var listAC = SelectDynamicData(data, new List<string> { "A", "C" });
//Produces
//{
// { 1, 2, 3},
// { 1.1, 2.2, 3.3 }
//}
}
You can use reflection, for example
string key = "A";
var query = data.Select(x =>
{
var prop = x.GetType().GetProperty(key); //NOTE: if key does not exist this will return null
return prop.GetValue(x);
});
foreach (var value in query)
{
Console.WriteLine(value); //will print 1, 2, 3
}

Remove list in list

I have a list that stores an instance of the list. I want to remove the object from the sublist with IdName == "149"
List<Product> productList = new List<Product>()
{
new Product()
{
Id = 1,
Model = "Phone",
TypeProd = new CheckTypes() { ChTypes = new List<CHType> { new CHType() { Id = 8, IdName = "261"}, new CHType () {Id = 9 , IdName = "149" } } }
},
new Product()
{
Id = 1,
Model = "Printer",
TypeProd = new CheckTypes() { ChTypes = new List<CHType> { new CHType() { Id = 8, IdName = null}, new CHType () {Id = 8 , IdName = "261" } } }
}
};
var pr = productList.Select(s => s.TypeProd).Where(w => w.ChTypes.Any(a => a.IdName != null && a.IdName.Contains("149"))).ToList();
// I
var pr0 = pr.Select(s => s.ChTypes).Where(w => w.Any(a => a.Id == 9)).ToList();
// II
var pr1 = pr.Select(s => s.ChTypes).Except(pr0);
// III
pr.Select(s=>s.ChTypes).ToList().RemoveAll(a => a.Any(item => item.IdName.Contains("149")));
foreach (var item in pr)
{
foreach (var item2 in item.ChTypes)
{
Console.WriteLine(item2.IdName);
}
Console.WriteLine("End");
}
Console.ReadKey();
I get to delete the whole sequence, but how to delete one element from the sequence?
Use Remove() to remove a given item, or use RemoveAt() to remove the item at a given location.
I think, you are complicating it more than it needs to be. You simply need to loop over all the ChTypes List and remove the unwanted ChType. It can be easily accomplished by the below code
foreach (var chType in productList.Select(prod => prod.TypeProd.ChTypes))
chType.RemoveAll(c => c.IdName != null && c.IdName.Contains("149"));
All your LINQ lines for pr, pr0, pr1 and pr.Select().ToList().RemoveAll() are all unnecessary. Also, in your current code you are picking up only CHType with Id = 9, not sure if that is by mistake or your code is correct but your question missed specifying it.
Since the OP insists on avoiding foreach below is a a bad LINQ way of doing it
productList.Select(prod => prod.TypeProd.ChTypes)
.Select(chType => chType.RemoveAll(c => c.IdName != null && c.IdName.Contains("149")))
.ToList();

C# take group with the same field form list

I would like to take, from a list, a group of elements which have the same field (direction field - look to code), and then take first item which occurs in list (myList order), checking if it is ok with if and take it.
Next, I want to take next direction (if exist), create a group and again take first element.
I don't know how many groups it will be at any step. I just know it will be max 4 group. How can I do this?
List <myClass> myList = allCreatedObjects;
class myClass
{
Control c;
Direction d;
}
public enum Direction
{
up, down, right, left,
}
I'm not sure what you want exactly. but if you want to group by direction, then in each direction check some condition, you may try as following:
var output = new Dictionary<Direction, List<myClass>>();
foreach (myClass cls in myList)
{
//check some condition based on cls properties
if (cls.c is TextBox && cls.d != Direction.down)
{
output[cls.d].Add(cls); //add it to output
}
}
//each item in output[direction] is of type List<myClass>
var upList = output[Direction.up]; //this is as List<myClass>
//var downList = output[Direction.down]; //this is as List<myClass>
//...
You could use linq, for example to select right:
var rightGroup = myList.Where(c => c.d == Direction.right);
If you want to do this automatically for all the enum values, you can use a loop like:
foreach (Direction direction in Enum.GetValues(typeof(Direction)))
{
var groupList = myList.Where(c => c.d == direction);
}
This will result in a couple of variables.
You can also use linq like this:
var group = from item in myList
group item by item.d into g
select new { key = g.Key, listItems = g.ToList() };
This will result in an object, with the direction as key, and listItems filled with the items. If you want empty lists for the non-added directions you'll need to join with the direction enum.
As for a more exotic example:
var group = from item in myList
where item.d == Direction.left //some condition on item
group item by item.d into g
where g.Any(c => c.d == Direction.up) //some condition on the group
select new { key = g.Key, values = g.ToList() };
List<myClass> myList = new List<myClass>
{
new myClass(){c = new Button(), d = Direction.down },
new myClass(){c = new Button(), d = Direction.left },
new myClass(){c = new Button(), d = Direction.right },
new myClass(){c = new Button(), d = Direction.up },
new myClass(){c = new TextBox(), d = Direction.down },
new myClass(){c = new TextBox(), d = Direction.left },
new myClass(){c = new TextBox(), d = Direction.right },
new myClass(){c = new TextBox(), d = Direction.up },
};
public class myClass
{
public Control c;
public Direction d;
}
public enum Direction
{
up, down, right, left,
}
private void button1_Click(object sender, EventArgs e)
{
var groups = myList.GroupBy(my => my.d);
var firstElems = groups.Select(g => g.FirstOrDefault()).ToList();
}

Update list by another list (linq)

I have List of object of class "Data" that look like:
class Data
{
int code;
string name;
...
DateTime date_update;
}
and I have another list of class, like:
class RefCodes
{
int old_code;
int new_code;
string new_name;
DateTime date_update;
}
The list of "Data" contains like 1,000 objects.
The list of "RefCodes" contains like 30 objects.
I need to replace in list "Data",
the fields:
"code" to be with value of "new_code",
and the "name" to be with value of "new_name".
The replacement need to be only for the objects that their code exist in list "RefCodes".
by the query: if code in Data.code == RefCodes.old_code
How can I do it?
I think you're looking for this:
foreach (var rcodeObj in RefCode)
{
foreach(var obj in (Data.Where(t => t.code == rcodeObj.old_code)))
{
obj.code = rcodeObj.new_code;
obj.name = rcodeObj.new_name;
}
}
If you are using C#6 you could use linq to do something like this
var updatedData = data.Select(x => new Data
{
code = refCodes.FirstOrDefault(y => y.old_code == x.code)?.new_code ?? x.code,
name = refCodes.FirstOrDefault(y => y.old_code == x.code)?.new_name ?? x.name,
});
You can use the following code:
foreach (var x in DataList)
{
var itemRefCode = RefCodesList.FirstOrDefault(d => d.old_code == x.code);
if (itemRefCode != null)
{
x.code = itemRefCode.new_code;
x.name = itemRefCode.new_name;
}
}
You can iterate through each of the lists and update the values as follows. Here I am using some sample inputs as shown below. Note that I am considering the fields of the classes to be public, for simplicity:
List<Data> dataList = new List<Data>
{
new Data { code = 1, name = "A" },
new Data { code = 2, name = "B" },
new Data { code = 10, name = "C" },
};
List<RefCodes> refList = new List<RefCodes>
{
new RefCodes { old_code = 1, new_code = 11, new_name = "X" },
new RefCodes { old_code = 2, new_code = 22, new_name = "Y" }
};
Console.WriteLine("Before");
dataList.ForEach(data => Console.WriteLine(data.code + ": " + data.name));
Console.WriteLine("");
Here is the code to do the updating:
foreach (var refCodes in refList)
{
foreach (var data in dataList)
{
if (data.code == refCodes.old_code)
{
data.code = refCodes.new_code;
data.name = refCodes.new_name;
}
}
}
Console.WriteLine("After");
dataList.ForEach(data => Console.WriteLine(data.code + ": " + data.name));
Output:
Before
1: A
2: B
10: C
After
11: X
22: Y
10: C
Would this solve your problem:
public void Update( List<Data> data, List<RefCodes> refCodes )
{
List<RefCodes> differences = refCodes
.Where( r => data.Any( d => r.old_code == d.code ) )
.ToList();
differences.ForEach( ( RefCodes item ) =>
{
Data element = data.FirstOrDefault( d => d.code == item.old_code );
element.code = item.new_code;
element.name = item.new_name;
} );
}
What you need is a Left Outer Join.
For example,
IEnumerable<Data> query = from data in dataList
join refCode in refList on data.code equals refCode.old_code into joined
from subCode in joined.DefaultIfEmpty()
select new Data
{
code = subCode?.new_code ?? data.code,
name = subCode?.new_name ?? data.name,
date_update = subCode == null ? data.date_update : DateTime.Now
};
will return a sequence with the result you expect.
**Let say tempAllocationR is list 1 and tempAllocationV is List2 **
var tempAllocation = new List<Object>();
if (tempAllocationR.Count > 0 && tempAllocationV.Count > 0)
{
foreach (TempAllocation tv in tempAllocationV)
{
var rec = tempAllocationR.FirstOrDefault(tr => tr.TERR_ID == tv.TERR_ID && tr.TERR == tv.TERR && tr.Team == tv.Team);
if (rec != null)
{
rec.Vyzulta = tv.Vyzulta;
}
else
{
tempAllocationR.Add(tv);
}
}
tempAllocation = tempAllocationR;
}
else if (tempAllocationV.Count == 0 && tempAllocationR.Count > 0)
{
tempAllocation = tempAllocationR;
}
else if (tempAllocationR.Count == 0 && tempAllocationV.Count > 0)
{
tempAllocation = tempAllocationV;
}

unequal size lists to merge

I have searched without success to a similar situation as follows.
I have two lists, list A and list B.
List A is composed of 10 objects created from ClassA which contains only strings.
List B is composed of 100 objects created from ClassB which only contains decimals.
List A is the header information.
List B is the data information.
The relationship between the two lists is:
Row 1 of list A corresponds to rows 1-10 of list B.
Row 2 of list A corresponds to rows 11-20 of list B.
Row 3 of list A corresponds to rows 21-30 of list B.
etc.........
How can I combine these two lists so that when I display them on the console the user will see a header row followed immediately by the corresponding 10 data rows.
I apologize if this has been answered before.
Ok, that should work. Let me know in case I got anything wrong.
List<ClassA> listA = GetListA()// ...
List<ClassB> listB = GetListA()// ...
if(listB.Count % listA.Count != 0)
throw new Exception("Unable to match listA to listB");
var datasPerHeader = listB.Count / listA.Count;
for(int i = 0; i < listA.Count;i++)
{
ClassA header = listA[i];
IEnumerable<ListB> datas = listB.Skip(datasPerHeader*i).Take(datasPerHeader);
Console.WriteLine(header.ToString());
foreach(var data in datas)
{
Console.WriteLine("\t{0}", data.ToString());
}
}
Here is some code that should fulfill your request - I am going to find a link for the partition extension as I can't find it in my code anymore:
void Main()
{
List<string> strings = Enumerable.Range(1,10).Select(x=>x.ToString()).ToList();
List<decimal> decimals = Enumerable.Range(1,100).Select(x=>(Decimal)x).ToList();
var detailsRows = decimals.Partition(10)
.Select((details, row) => new {HeaderRow = row, DetailsRows = details});
var headerRows = strings.Select((header, row) => new {HeaderRow = row, Header = header});
var final = headerRows.Join(detailsRows, x=>x.HeaderRow, x=>x.HeaderRow, (header, details) => new {Header = header.Header, Details = details.DetailsRows});
}
public static class Extensions
{
public static IEnumerable<List<T>> Partition<T>(this IEnumerable<T> source, Int32 size)
{
for (int i = 0; i < Math.Ceiling(source.Count() / (Double)size); i++)
yield return new List<T>(source.Skip(size * i).Take(size));
}
}
That Partition method is the one that does the grunt work...
And here is the link to the article - LINK
EDIT 2
Here is better code for the Main() method... Rushed to answer and forgot brain:
void Main()
{
List<string> strings = Enumerable.Range(1,10).Select(x=>x.ToString()).ToList();
List<decimal> decimals = Enumerable.Range(1,100).Select(x=>(Decimal)x).ToList();
var detailsRows = decimals.Partition(10);
var headerRows = strings; //just renamed for clarity from other code
var final = headerRows.Zip(detailsRows, (header, details) => new {Header = header, Details = details});
}
This should be pretty straight forward unless I'm missing something.
var grouped = ListA.Select((value, index) =>
new {
ListAItem = value,
ListBItems = ListB.Skip(index * 10).Take(10)
})
.ToList();
Returns back an anonymous type you can loop through.
foreach (var group in grouped)
{
Console.WriteLine("List A: {0}", group.Name);
foreach (var listBItem in group.ListBItems)
{
Console.WriteLine("List B: {0}", listBItem.Name);
{
}
The easiest way may be something like this:
var listA = new List<string>() { "A", "B", "C", ... }
var listB = new List<decimal>() { 1m, 2m, 3m, ... }
double ratio = ((double)listA.Count) / listB.Count;
var results =
from i in Enumerable.Range(0, listB.Count)
select new { A = listA[(int)Math.Truncate(i * ratio)], B = listB[i] };
Or in fluent syntax:
double ratio = ((double)listA.Count) / listB.Count;
var results = Enumerable.Range(0, listB.Count)
.Select(i => new { A = listA[(int)Math.Truncate(i * ratio)], B = listB[i] });
Of course if you know you will always have 10 items in listB for each item in listA, you can simplify this to:
var results =
from i in Enumerable.Range(0, listB.Count)
select new { A = listA[i / 10], B = listB[i] };
Or in fluent syntax:
var results = Enumerable.Range(0, listB.Count)
.Select(i => new { A = listA[i / 10], B = listB[i] });
This will return a result set like
{ { "A", 1 },
{ "A", 2 },
{ "A", 3 }
..,
{ "A", 10 },
{ "B", 11 },
{ "B", 12 },
{ "B", 13 },
...
{ "B", 20 },
{ "C", 21 },
...
{ "J", 100 }
}

Categories

Resources