Here is what I am trying to do but with no success. I want to call from x in list1 and join y in list2 where regex.Match(x.Value).Success. After these steps I need to call String.Replace twice on x.Value. Then call the on and select operators. I want it to look something like the second example. How can this be acheived?
Here is how my lists look like:
list1 list2
Name Value Name Value
item.1 $(prod1) prod1 prodVal1
item.2 $(prod2) prod2 prodVal2
item.3 prod3 prod3 prodVal3
Here is how my lists should look like:
updatedList
Name Value
item.1 prodVal1
item.2 prodVal2
item.3 prod3
Example 1 (This is what I currently have):
foreach (var x in list1)
{
Match match = reg.Match(x.Value);
if (match.Success)
{
x.Value = x.Value.Replace("$(", "");
x.Value = x.Value.Replace(")", "");
}
}
var commonItems = from x in list1
join y in list2
on x.Value equals y.Name
//where regex.Match(x.Value).Success
select new { Item = x, NewValue = y.Value };
foreach (var x in commonItems)
{
x.Item.Value = x.NewValue;
}
Example 2:
var commonItems = from x in list1
join y in list2
where regex.Match(x.Value).Success
//do x.Value.Replace("$(", "")
//do x.Value.Replace(")", "")
//then call
on x.Value equals y.Name
select new { Item = x, NewValue = y.Value };
foreach (var x in commonItems)
{
x.Item.Value = x.NewValue;
}
If I understood correctly what you want is to use let in your query:
var commonItems = from x in list1
join y in list2
let newX = x.Value.Replace("$(", "").Replace(")", "")
where regex.Match(x.Value).Success
&& newX == y.Name
select new { Item = newX, NewValue = y.Value };
Are you sure you need Regex.Match? you could obtain the same result without using it, anyway I added both versions...
In the 1° version you can use a simple If statement to check if the value was changed
NewValue = x.Value != x1 ? y.Value: x.Value
Sample code
given this class
class MyClass
{
public string Name { get; set; }
public string Value { get; set; }
}
.
adding items
var list1 = new List<MyClass>();
list1.Add(new MyClass { Name = "item.1", Value = "$(prod1)" } );
list1.Add(new MyClass { Name = "item.2", Value = "$(prod2)" });
list1.Add(new MyClass { Name = "item.3", Value = "prod3" });
var list2 = new List<MyClass>();
list2.Add(new MyClass { Name = "prod1", Value = "prodVal1" });
list2.Add(new MyClass { Name = "prod2", Value = "prodVal2" });
list2.Add(new MyClass { Name = "prod3", Value = "prodVal3" });
getting common list
var q = from x in list1
let x1 = x.Value.Replace("$(", "").Replace(")", "")
join y in list2 on x1 equals y.Name
select new {
Item = x.Name,
NewValue = x.Value != x1 ? y.Value: x.Value
};
foreach (var s in q)
{
Console.WriteLine(s.Item + " " + s.NewValue);
}
result
item.1 prodVal1
item.2 prodVal2
item.3 prod3
PS: I don't think you need Regex, but just in case this version will work using it.
var q = from x in list1
let x1 = x.Value.Replace("$(", "").Replace(")", "")
join y in list2 on x1 equals y.Name
select new
{
Item = x.Name,
NewValue = Regex.Match(x.Value, x1).Success ? x.Value : y.Value
};
Related
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;
}
Actually, a colleague of mine asked me this question and I haven't been able to come up with an answer. Here goes.
Given an entity with 2 foreign keys, say
public class MyTable
{
public int Key1 { get; set; }
public int Key2 { get; set; }
}
and 2 lists
public ICollection List1 => new List<int> { 1, 2, 3 };
public ICollection List2 => new List<int> { 4, 5, 6 };
he needs to query for all records where Key1 matches the value from List1 and Key2 matches the value from List2, e.g.
Key1 == 1 && Key2 == 4
that is, he wants to check for any given tuple from List1 and List2, (1, 4), (2, 5) and (3, 6).
Is there a straightforward way in EF to do this?
You can make a for loop to capture some local variable (in each loop) in the Where and use Concat (or Union - maybe with worse performance) to sum up all the result like this:
IQueryable<MyTable> q = null;
//suppose 2 lists have the same count
for(var i = 0; i < list1.Count; i++){
var k1 = list1[i];
var k2 = list2[i];
var s = context.myTables.Where(e => e.Key1 == k1 && e.Key2 == k2);
q = q == null ? s : q.Concat(s);
}
//materialize the result
var result = q.ToList();
NOTE: we can use Concat here because each sub-result should be unique (based on searching the keys). It surely has better performance than Union (ensuring uniqueness while we already know the sub-results are all unique beforehand - so it's unnecessary).
If you have a list of int (just integral numeric), you can also pair the keys into underscore separated string and use Contains normally like this:
var stringKeys = list1.Select((e,i) => e + "_" + list2[i]).ToList();
var result = context.myTables.Where(e => stringKeys.Contains(e.Key1 + "_" + e.Key2))
.ToList();
Building an Expression tree is also another approach but it's more complicated while I'm not sure if it has better performance.
Try this:
static IQueryable<TSource> WhereIn(this Table<TSource> table, List<object[]> list) where TSource : class
{
var query = table.AsQueryable();
foreach (object[] item in list)
{
Expression<Func<TSource, bool>> expr = WhereInExpression(item);
query = query.Where(expr);
}
return query;
}
static Expression<Func<TSource, bool>> WhereInExpression<TSource>(object[] item)
{
ParameterExpression parameterItem = Expression.Parameter(typeof(TSource), "expr");
BinaryExpression filter1 = Expression.Equal(LambdaExpression.PropertyOrField(parameterItem, "Key1"),
Expression.Constant(item[0]));
BinaryExpression filter2 = Expression.Equal(LambdaExpression.PropertyOrField(parameterItem, "Key2"),
Expression.Constant(item[1]));
BinaryExpression filter = LambdaExpression.And(filter1, filter2);
var expr = Expression.Lambda<Func<TSource, bool>>(filter, new ParameterExpression[] { parameterItem });
expr.Compile();
return expr;
}
Usage:
List<object[]> list = new List<object[]>() { new object[] { 1, 100 }, new object[] { 1, 101 }, new object[] { 2, 100 } };
var result = db.MyTable.WhereIn<MyTable>(list);
Confirmed to work with Entity framework
var tuples = List1.Cast<int>().Zip(List2.Cast<int>(), (l, r) => new { l, r });
var results = Orders.Where(o =>
tuples.Contains(new { l = (int)o.KeyOne, r = (int)o.KeyTwo })
);
Or simpler, if you define your lists as ICollection<int> or IList<int> (etc...):
var tuples = List1.Zip(List2, (l, r) => new { l, r });
var results = Orders.Where(o =>
tuples.Contains(new { l = (int)o.KeyOne, r = (int)o.KeyTwo })
);
Fiddle here : https://dotnetfiddle.net/YyyZBY
var List_1 = new List<int> { 1, 2, 3 };
var List_2 = new List<int> { 4, 5, 6 };
var TargetList = new List<MyTable>();
var index1=0;
var List1 = List_1.Select(x=>new { ind=index1++,value=x });
var index2=0;
var List2 = List_2.Select(x=>new { ind=index2++,value=x });
var values = from l1 in List1
join l2 in List2 on l1.ind equals l2.ind
select new {value1=l1.value,value2=l2.value };
var result = TargetList.Where(x=>values.Any(y=>y.value1==x.Key1&&y.value2==x.Key2));
How can I perform a conditional select on a column value, where I have a preference over which value is returned. If I can't find the top choice, I settle on the next, if available, and then if not the next, etc. As it looks right now, it would take 3 total queries. Is there a way to simplify this further?
var myResult = string.Empty;
if (myTable.Where(x => x.ColumnValue == "Three").Any())
{
myResult = "Three"; // Can also be some list.First().Select(x => x.ColumnValue) if that makes it easier;
}
else if (myTable.Where(x => x.ColumnValue == "One").Any())
{
myResult = "One";
}
else if (myTable.Where(x => x.ColumnValue == "Two").Any())
{
myResult = "Two";
}
else
{
myResult = "Four";
}
You could use a string[] for your preferences:
string[] prefs = new[]{ "One", "Two", "Three" };
string myResult = prefs.FirstOrDefault(p => myTable.Any(x => x.ColumnValue == p));
if(myResult == null) myResult = "Four";
Edit Enumerable.Join is a very efficient hash table method, it also needs only one query:
string myResult = prefs.Select((pref, index) => new { pref, index })
.Join(myTable, xPref => xPref.pref, x => x.ColumnValue, (xPref, x) => new { xPref, x })
.OrderBy(x => x.xPref.index)
.Select(x => x.x.ColumnValue)
.DefaultIfEmpty("Four")
.First();
Demo
I wrote an extension method that effectively mirrors Tim Schmelter's answer (was testing this when he posted his update. :-()
public static T PreferredFirst<T>(this IEnumerable<T> data, IEnumerable<T> queryValues, T whenNone)
{
var matched = from d in data
join v in queryValues.Select((value,idx) => new {value, idx}) on d equals v.value
orderby v.idx
select new { d, v.idx };
var found = matched.FirstOrDefault();
return found != null ? found.d : whenNone;
}
// usage:
myResult = myTable.Select(x => x.ColumnValue)
.PreferredFirst(new [] {"Three", "One", "Two"}, "Four");
I've written one that will quit a little more early:
public static T PreferredFirst<T>(this IEnumerable<T> data, IList<T> orderBy, T whenNone)
{
// probably should consider a copy of orderBy if it can vary during runtime
var minIndex = int.MaxValue;
foreach(var d in data)
{
var idx = orderBy.IndexOf(d);
if (idx == 0) return d; // best case; quit now
if (idx > 0 && idx < minIndex) minIndex = idx;
}
// return the best found or "whenNone"
return minIndex == int.MaxValue ? whenNone : orderBy[minIndex];
}
I use a weighted approach in SQL where I assign a weight to each conditional value. The solution would then be found by finding the highest or lowest weight depending on your ordering scheme.
Below would be the equivalent LINQ query. Note that in this example I am assigning a lower weight a higher priority:
void Main()
{
// Assume below list is your dataset
var myList =new List<dynamic>(new []{
new {ColumnKey=1, ColumnValue ="Two"},
new {ColumnKey=2, ColumnValue ="Nine"},
new {ColumnKey=3, ColumnValue ="One"},
new {ColumnKey=4, ColumnValue ="Eight"}});
var result = myList.Select(p => new
{
ColVal = p.ColumnValue,
OrderKey = p.ColumnValue == "Three" ? 1 :
p.ColumnValue == "One" ? 2 :
p.ColumnValue == "Two" ? 3 : 4
}).Where(i=> i.OrderKey != 4)
.OrderBy(i=>i.OrderKey)
.Select(i=> i.ColVal)
.FirstOrDefault();
Console.WriteLine(result ?? "Four");
}
How about something like this:
var results = myTable.GroupBy(x => x.ColumnValue).ToList();
if (results.Contains("Three")) {
myResult = "Three";
} else if (results.Contains("One")) {
myResult = "One";
} else if (results.Contains("Two")) {
myResult = "Two";
} else {
myResult = "Four";
}
I have written following linq query but I am not getting expected result.My Expected result is all the matching record bettween lst and list and all the non matching record from list
for example.
I want following result
a,b,c,d,e,f
public class Com : IEqualityComparer<DuplicateData>
{
public bool Equals(DuplicateData x, DuplicateData y)
{
return x.address.Equals(y.address);
}
public int GetHashCode(DuplicateData obj)
{
return obj.address.GetHashCode();
}
}
static void Run ()
{
List<string> lst = new List<string>();
lst.Add("a");
lst.Add("b");
lst.Add("c");
lst.Add("p");
List<DuplicateData> list = new List<DuplicateData>()
{
new DuplicateData{address="a"},
new DuplicateData{address="a"},
new DuplicateData{address="a"},
new DuplicateData{address="b"},
new DuplicateData{address="b"},
new DuplicateData{address="c"},
new DuplicateData{address="d"},
new DuplicateData{address="e"},
new DuplicateData{address="f"},
};
var dup = list.Distinct(new Com());
var RightJoin = from x in dup
join y in lst
on x.address equals y
into right
from z in right
select new
{
UniqueAddress = z,
};
}
Try it like this:
var RightJoin = from x in dup
join y in lst
on x.address equals y
into right
from z in right.DefaultIfEmpty(x.address)
select new
{
UniqueAddress = z,
};
result is (a,b,c,d,e,f)
Working sample: http://ideone.com/MOIhZH
Explanation
To make a left/right join in linq you have to use DefaultIfEmpty method, that will yield (default) result when there is no match (the joined result is empty). However, default value for string is null so you have to provide default value from the "left side" collection to see it in the result set.
Alternative approach
This is probably more convenient approach. Instead of selecting from z and providing the default value, you will select from x.address - the left side of the join.
var RightJoin = from x in dup
join y in lst
on x.address equals y
into right
from z in right.DefaultIfEmpty()
select new
{
UniqueAddress = x.address,
};
This should work:
var res = from x in list
join y in lst on x.address equals y into right
from xy in right.DefaultIfEmpty()
select new { UniqueAddress = xy };
result:
{ UniqueAddress = a },
{ UniqueAddress = a },
{ UniqueAddress = a },
{ UniqueAddress = b },
{ UniqueAddress = b },
{ UniqueAddress = c },
{ UniqueAddress = null },
{ UniqueAddress = null },
{ UniqueAddress = null }
My object is "MyClass" with 2 properties : Id (int) and HourBy (int)
I have two list :
var List1 = new List<MyClass>();
var List2 = new List<MyClass>();
I'd like to get one list with :
- Object from List1 present in List2 based on Id with Hourby (list2) < Hourby in List1
- Object from List1 no present in List2
//#Sample1
//List1 :
List1.add(new MyClass(1,10));
List1.add(new MyClass(2,20));
List1.add(new MyClass(3,30));
//List2 :
List2.add(new MyClass(1,10));
List2.add(new MyClass(2,15));
//I'd like to get :
new MyClass(2,5);
new MyClass(3,30);
//Sample2
List1 :
List1.add(new MyClass(1,10));
List1.add(new MyClass(2,20));
List1.add(new MyClass(3,30));
//List2 :
List2.add(new MyClass(1,10));
List2.add(new MyClass(2,15));
List2.add(new MyClass(2,2));
//I'd like to get :
new MyClass(2,3);
new MyClass(3,30);
Thanks,
You can do this by the following Linq statement:
var result = List1.Where(x => !List2.Select(y => y.Id).Contains(x.Id)
|| List2.Single(y => x.Id == y.Id).HourBy < x.HourBy);
Best Regards
I think this is what you're after, based on the example lists you supplied...
var list3 = new List<MyClass>();
foreach(var item in list1)
{
var totalFromOther = list2.Where(x => x.id == item.id).Sum(y => y.HourBy);
var newItem = new MyClass() { id = item.Id, HourBy = item.HourBy - totalFromOther };
if(newItem.HourBy > 0)
list3.Add(newItem);
}
Here's a solution:
var qry = from ee in
(from e1 in list1
join e2 in list2 on e1.Id equals e2.Id into gj
from e in gj.DefaultIfEmpty()
select new {
e1.Id,
MainHourBy = e1.HourBy,
SecondHourBy = (e == null ? 0 : e.HourBy)
})
where ee.SecondHourBy < ee.MainHourBy
group ee by ee.Id into g
select new MyClass
{
Id = g.Key,
HourBy = g.First().MainHourBy - g.Sum(el => el.SecondHourBy)
};
I use this code, it's not perfect but working and may be clearer for newbie in Linq (like me)
List< MyClass> listRes = new List< MyClass>();
foreach ( MyClass item in list1)
{
var exist = list2
.Select(x => x.MyClass)
.Contains(item);
if (!exist)
listRes.Add(item);
else
{
var totalFromOther = list2
.Where(x => x.MyClass.Id == item.Id)
.Sum(y => y.HourBy);
if (item.HourBy != totalFromOther)
{
item.HourBy = item.HourBy - totalFromOther;
listRes.Add(item);
}
}
}
Thanks for you help,
var agg1 = from l in list1
group l by l.ID into g
select new { g.Key, Sum = g.Sum(_ => _.HourBy) };
var agg2 = from l in list2
group l by l.ID into g
select new { g.Key, Sum = g.Sum(_ => _.HourBy) };
var q = from l1 in agg1
let l2 = agg2.FirstOrDefault(_ => _.Key == l1.Key)
let sum = l1.Sum - (l2 != null ? l2.Sum : 0)
where sum != 0
select new MyClass(l1.Key, sum);
var list3 = q.ToList();
//Group list 2, so there is one entry per ID, with HourBy = total hour by
var summedL2 = from item2 in List2
group item2 by item2.ID into g
select new Test(g.Key, g.Sum(item => item.HourBy));
var result =
//Select items from list1
from item1 in List1
//Left join to our summed List2
join item2s in summedL2 on item1.ID equals item2s.ID into tempItem2s
from item2 in tempItem2s.DefaultIfEmpty()
//Where there is no match, or item2 is < item1
where (item2 == null || item2.HourBy < item1.HourBy)
//Select a new Test object, with the HourBy changed
select new Test(item1.ID, item1.HourBy - (item2 == null ? 0 : item2.HourBy));