Linq to object Right outer join - c#

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 }

Related

Join Two Different Object DataType using Linq in C#

I am having two lists:
ListA:
[
{
Id = 1,
Name = "A",
Summary = ""
},
{
Id = 2,
Name = "B",
Summary = ""
}
]
ListB:
[
{
Id = 1,
Value = "SomeThing"
},
{
Id = 2,
Value = "EveryThing"
}
]
I want to join that two list using LINQ and want to return ListA which value is update as Below
[
{
Id = 1,
Name = "A",
Summary = "SomeThing"
},
{
Id = 2,
Name = "B",
Summary = "EveryThing"
}
]
I am joining ListA and ListB based on Id and assigning value to summary.
I tried below approach:
var query = from obj1 in ListA
join obj2 in ListB on obj1.Id equals obj2.Id
select obj1.Summary = obj2.Value, return obj1;
**=>so here i want assign data from obj2 to obj1 then want to return obj1 **
is that possible or how we can do this?
You could also update the existing ListA with a simple loop
foreach (var itemA in ListA)
{
itemA.Summary = ListB.FirstOrDefault(x => x.Id == itemA.Id)?.Value;
}
Join approach
var query = ListA.Join(ListB,
ia => ia.Id,
ib => ib.Id,
(ia, ib) => new aItem() //type of ListA here
{
Id = ia.Id,
Name = ia.Name,
Summary = ib.Value
});
You could try to join the two lists like this:
var listA = new List<ClassA>();
var listB = new List<ClassB>();
var list = listA.Join(listB, a => a.Id, b => b.Id, (a, b) =>
new ClassA
{
Id = a.Id,
Name = a.Name,
Summary = b.Value
});
Using method syntax Enumerable.Join is the easier one to use here:
var result = listA.Join(listB, // Join ListA and ListB
a => a.Id, // from every a in ListA take a.Id
b => b.Id, // from every b in ListB take b.Id
(a, b) => new // when they match, take the a and the b
{ // to create a new object with properties
Id = a.Id,
Name = a.Name,
Summary = b.Value,
});
Note that the result is of an anonymous type, If you want a result with the same type as the items in ListA (let's say they are of class A), change the last part of the join:
(a, b) => new A() // when they match, take the a and the b
{ // to create a new object of class A
Id = a.Id,
Name = a.Name,
Summary = b.Value,
});

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

linq selectmany flatten multiple levels

I have the following relation (for example)
A contains one or more B's
Each B contains one or more C's and D's
I want to flatten everything using SelectMany along with some search conditions and get A,B,C and D's . This is what i have.
context.A.Where(a => (string.IsNullOrEmpty(name) || a.Name.Contains(name)))
.SelectMany(ab =>ab.b.Where(n=>n.bname.Contains(name) || string.IsNullOrEmpty(name)),
(aa, bb) => new { aa, bb }) //gets all a's and b's
.SelectMany(bc => bb.c.Where(w => w.KEYWORD.Contains(Keyword) || string.IsNullOrEmpty(Keyword)),
(bc,words) => new {bc,kwords}) //gets all b's and c's
Is what i am doing right? If so , then how to get B along with all D's adding to the above expression?
Data Selection using Lambda Syntax:
var flatData = context.A.SelectMany(a => a.B.SelectMany(b => b.Select(new {a,b,c = b.C,d = b.D})
Going further, following checks shall be done before applying the Where Clause, as they check the constant input supplied, name and keyword
string.IsNullOrEmpty(name)
string.IsNullOrEmpty(keyword)
Remaining checks would be simple:
if(!string.IsNullOrEmpty(name))
flatData = flatData.Where(data => data.a.Name.Contains(name))
.Where(data => data.b.Name.Contains(name));
if(!string.IsNullOrEmpty(keyword))
flatData = flatData.Where(data => data.c.Keyword.Contains(keyword));
Important points:
flatData above has a cascading filter, first on a.Name, b.Name and c.Keyword
Agreeing with what Ivan suggested you can flatten this 3 levels deep structure like this:
var query = (from a in A
from b in (List<dynamic>)a.b
from c in (List<dynamic>)b.c
from d in (List<dynamic>)b.d
select new { a, b, c, d });
if (!string.IsNullOrEmpty(name))
{
query = query.Where(record => record.b.bname.Contains(name));
}
if (!string.IsNullOrEmpty(keyword))
{
query = query.Where(record => record.c.keyword.Contains(keyword));
}
var result = query.ToList();
You can also add the where clauses in the query at the top but seeing that you are checking if you got any valid input at all I'd put it after
Tested it with this sample data:
List<dynamic> A = new List<dynamic>
{
new { b = new List<dynamic> { new { bname = "a", c = new List<dynamic> { new { keyword = "b" } }, d = new List<dynamic> { 1, 2, 3 } } } },
new { b = new List<dynamic> { new { bname = "a", c = new List<dynamic> { new { keyword = "d" } }, d = new List<dynamic> { 1, 2, 3 } } } }
};
string name = "a";
string keyword = "b";

How to do a String.Replace in LINQ?

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

Linq to SQL With Left Outer Join and Group By With Sum - How To

I'm trying to transform the SQL Query below into Linq to SQL
select Categorias.IdCategoria, Categorias.Nome, SUM(lancamentos.valor)
from lancamentos
left outer join Categorias on Lancamentos.IdCategoria = Categorias.IdCategoria
where Month(DataLancamento) = 11
and Credito = 1
and Lancamentos.Ocultar = 0
group by Categorias.IdCategoria, Categorias.Nome
This is what I've done
from lancamento in Lancamentos
where lancamento.Credito == true
&& lancamento.Ocultar == false
&& lancamento.DataLancamento.Month == 10
join categoria in Categorias on lancamento.IdCategoria equals categoria.IdCategoria into temp
from lancamentoJoinCategoria in temp.DefaultIfEmpty()
group lancamentoJoinCategoria by new { lancamentoJoinCategoria.IdCategoria, lancamentoJoinCategoria.Nome } into x
select new {
IdCategoria = (int?)x.Key.IdCategoria
, Nome = x.Key.Nome
}
How do I add the SUM(lancamentos.valor) to the linq to sql above ?
It will be:
(from lancamento in Lancamentos
join categoria in Categorias on lancamento.IdCategoria equals categoria.IdCategoria into temp
from lancamentoJoinCategoria in temp.DefaultIfEmpty()
where lancamento.Credito == true
&& lancamento.Ocultar == false
&& lancamento.DataLancamento.Month == 10
group lancamento by new { lancamentoJoinCategoria.IdCategoria, lancamentoJoinCategoria.Nome } into x
select new
{
IdCategoria = (int?)x.Key.IdCategoria,
Nome = x.Key.Nome,
sumValor = x.Sum(a=>a.valor)
});
You use the .Sum() method.
Eg;
Public Sub LinqToSqlCount03()
Dim q = (From o In db.Orders _
Select o.Freight).Sum()
Console.WriteLine(q)
End Sub
according to MSDN there is no query expression equivalent to the Sum() operation.
I provided a little sample how you could use the Method Syntax of Sum() in a query.
Some query operations, such as Count
or Max, have no equivalent query
expression clause and must therefore
be expressed as a method call. Method
syntax can be combined with query
syntax in various ways. For more
information, see LINQ Query Syntax versus Method Syntax (C#).
var example = new[]
{
new { Count = 1, Name = "a" }, new { Count = 2, Name = "b" },
new { Count = 2, Name = "c" }, new { Count = 2, Name = "c" }
};
var result = from x in example
select new
{
x.Name,
Sum = (from y in example
where y.Count.Equals(2)
&& y.Name==x.Name
select y.Count).Sum()
};
var distinct = result.Distinct().ToList();

Categories

Resources