Join collections by id with linq [duplicate] - c#

This question already has answers here:
Join/Where with LINQ and Lambda
(10 answers)
Closed 5 years ago.
I have a object collection
var myData = new[]
{
new {
ID = "1285",
COUNT = 45
},
new {
ID = "1286",
COUNT = 156
},
new {
ID = "1287",
COUNT = 965
}
};
And my another collection comes from another data source like this:
var incomingData = new[]
{
new {
ID = "1285",
LOCATION = "City-1"
},
new {
ID = "1286",
LOCATION = "City-2"
},
new {
ID = "1287",
LOCATION = "City-3"
}
};
I want to change incoming data with COUNT property by Id.
var NewData = new[]
{
new {
ID = "1285",
LOCATION = "City-1",
COUNT = 45
},
new {
ID = "1286",
LOCATION = "City-2",
COUNT = 156
},
new {
ID = "1287",
LOCATION = "City-3",
COUNT = 965
}
};
How can I do this with linq labda functions. I do not want to use foreach loop. (myData and incomingData collection lengths may be different)

Just use ordinary join statements like this:
var result=
(
from data in myData
join inData in incomingData
on data.ID equals inData.ID
select new
{
data.ID,
inData.LOCATION,
data.COUNT
}
).ToList();
Easy, simple, readable

Related

Cartesian Product of an arbitrary number of objects [duplicate]

This question already has answers here:
Is there a good LINQ way to do a cartesian product?
(3 answers)
Closed 4 years ago.
I'm looking to get the Cartesian Product of an arbitrary number of objects in c#. My situation is slightly unusual - my inputs are not lists of base types, but objects which have a property that's a list of base types.
My input and output objects are as follows:
public class Input
{
public string Label;
public List<int> Ids;
}
public class Result
{
public string Label;
public int Id;
}
Some sample input data:
var inputs = new List<Input>
{
new Input { Label = "List1", Ids = new List<int>{ 1, 2 } },
new Input { Label = "List2", Ids = new List<int>{ 2, 3 } },
new Input { Label = "List3", Ids = new List<int>{ 4 } }
};
And my expected output object:
var expectedResult = new List<List<Result>>
{
new List<Result>
{
new Result{Label = "List1", Id = 1},
new Result{Label = "List2", Id = 2},
new Result{Label = "List3", Id = 4}
},
new List<Result>
{
new Result{Label = "List1", Id = 1},
new Result{Label = "List2", Id = 3},
new Result{Label = "List3", Id = 4}
},
new List<Result>
{
new Result{Label = "List1", Id = 2},
new Result{Label = "List2", Id = 2},
new Result{Label = "List3", Id = 4}
},
new List<Result>
{
new Result{Label = "List1", Id = 2},
new Result{Label = "List2", Id = 3},
new Result{Label = "List3", Id = 4}
}
};
If I knew the number of items in 'inputs' in advance I could do this:
var knownInputResult =
from id1 in inputs[0].Ids
from id2 in inputs[1].Ids
from id3 in inputs[2].Ids
select
new List<Result>
{
new Result { Id = id1, Label = inputs[0].Label },
new Result { Id = id2, Label = inputs[1].Label },
new Result { Id = id3, Label = inputs[2].Label },
};
I'm struggling to adapt this to an arbitrary number of inputs - is there a possible way to do this?
I consider this duplicate of question linked in comments, but since it was reopened and you struggle to adapt that question to your case, here is how.
First grab function by Eric Lippert from duplicate question as is (how it works is explained there):
public static class Extensions {
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] { item })
);
}
}
Then flatten your input. Basically just attach corresponding label to each id:
var flatten = inputs.Select(c => c.Ids.Select(r => new Result {Label = c.Label, Id = r}));
Then run cartesian product and done:
// your expected result
var result = flatten.CartesianProduct().Select(r => r.ToList()).ToList();
I'm not proud of the amount of time I spent messing with this, but it works.
It's basically black magic, and I would replace it the first chance you get.
public static List<List<Result>> Permutate(IEnumerable<Input> inputs)
{
List<List<Result>> results = new List<List<Result>>();
var size = inputs.Select(inp => factorial_WhileLoop(inp.Ids.Count)).Aggregate((item, carry) => item + carry) - 1;
for (int i = 0; i < size; i++) results.Add(new List<Result>());
foreach (var input in inputs)
{
for (int j = 0; j < input.Ids.Count; j++)
{
for (int i = 0; i < (size / input.Ids.Count); i++)
{
var x = new Result() { Label = input.Label, Id = input.Ids[j] };
results[(input.Ids.Count * i) + j].Add(x);
}
}
}
return results;
}
public static int factorial_WhileLoop(int number)
{
var result = 1;
while (number != 1)
{
result = result * number;
number = number - 1;
}
return result;
}

comparison of the list in the list

I have a list of lists:
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"
}
}
}
}
};
And I want to get the first item of this list by comparing the IdName elements with a string[]:
string[] arrStr = new string[] { "261", "149" };
How can I do this better? Tried using foreach and by creating a temporary object that takes an array value and then uses intersect to compare.
You could do it pretty simply with LINQ:
var product = productList
.FindAll(x => x.TypeProd.ChTypes
.All(y => arrString.Contains(y.IdName));
This will give you all products whose TypeProd.ChTypes elements are all in arrString.
For faster performance, you may want to turn arrString into a HashSet<string>.

Linq query - List within List [duplicate]

This question already has answers here:
Group by in LINQ
(11 answers)
Closed 5 years ago.
I'm trying to select a list that contains Fund.Name and List<Investment>.
var funds = new List<Fund>
{
new Fund { Id = 1 , Name = "good" },
new Fund { Id = 2, Name = "bad" }
};
var investments = new List<Investment>
{
new Investment { Fund = funds[0], Value = 100 },
new Investment { Fund = funds[0], Value = 200 },
new Investment { Fund = funds[1], Value = 300 }
};
Then I'm trying to create the query with this:
var query = from f in funds
join i in investments
on f.Id equals i.Fund.Id
select new { f.Name, i };
I wanted something like this:
{ Name = good, {{ Id = 1, Value = 100 }, { Id = 1, Value = 200 }}},
{ Name = bad, { Id = 2, Value = 300 }}
But I'm getting something like this:
{ Name = good, { Id = 1, Value = 100 }},
{ Name = good, { Id = 1, Value = 200 }},
{ Name = bad, { Id = 2, Value = 300 }}
Try using GroupJoin.
var query = funds.GroupJoin(investments, f => f.Id, i => i.Fund.Id, (f, result) => new { f.Name, result });

Convert Objects into Hierarchy based on Number Sequence

I have following data Structure and i want to put covert it in a Hierarchy based on RelationId. This is sorted on RelationId.
Id = 2 has relationId =2 and following two rows has realtionId =0 . That represent the Id=3 and Id=4 are child of Id = 2
Id Name RelationId SortOrder
1 A 1 1
2 B 2 2
3 C 0 3
4 D 0 4
5 E 3 5
6 F 0 6
7 G 0 7
8 H 4 8
End Result would be like following
Id = 1
|
Id = 2
|___ Id = 3 , Id = 4
Id = 5
|___ Id= 6 , Id=7
Id = 8
The desired result is as following (for simplicity representing it as List). This would be a List<Something> in C#
Result =
[
{ Id = 1, Name = A, Children = Null },
{ Id = 2, Name = B, Children = [{ Id = 3, Name = C }, {Id = 4, Name = D }] },
{ Id = 5, Name = E, Children = [{ Id = 6, Name = F }, {Id = 7, Name = G }] },
{ Id = 8, Name = H}
]
My unsuccessful attempt is as following
var finalResult = new List<sampleDataClass>();
var sampleData = GetMeSampleData();
var count = sampleData.Count();
foreach (var item in sampleData)
{
var alreadyExist = finalResult.Any(x => x.Id == item.Id);
var newObject = new sampleDataClass();
if (!alreadyExist && item.RelationId!= 0)
{
newObject = item;
}
for (int i = item.SortOrder; i < count; i++)
{
if (sampleData[i].RelationId== 0)
{
newObject.Children.Add(sampleData[i]);
}
}
finalResult.Add(newObject );
}
Since your RelationId decides whether it is a root or nested element, you could form a group based on these relation and do this. I would suggest using Linq
List<SomeData> somedata = ... // your data.
int index=0;
var results = somedata
.Select(x=> new {gid = x.RelationId ==0? index: ++index, item=x})
.GroupBy(x=> x.gid)
.Select(x=> {
var first = x.FirstOrDefault();
return new
{
Id = first.item.Id,
Name = first.item.Name,
Children = x.Skip(1).Select(s=> new {
Id = s.item.Id,
Name = s.item.Name,
})
};
})
.ToList();
Output :
Id=1, Name=A
Id=2, Name=B
Id=3, Name=C
Id=4, Name=D
Id=5, Name=E
Id=6, Name=F
Id=7, Name=G
Id=8, Name=H
Check this Working Code
I have done it like this. Don't know if there can be some more elegant solution
var data = new List<MyDataObject>();
var SampleData = GetMeSampleData;
var count = SampleData.Count();
for (int i=0;i<count;i++)
{
var rootAdded = false;
var relationId = SampleData[i].relationId;
var alreadyExist = data.Any(x => x.Id == SampleData[i].Id);
var mydataObject = new MyDataObject();
if (!alreadyExist && SampleData[i].RelationId != 0)
{
mydataObject = SampleData[i];
rootAdded = true;
}
for(int j=i+1;j<count;j++)
{
if ((SampleData[j].RelationId == 0 && rootAdded))
{
mydataObject.Children.Add(SampleData[j]);
}
if (SampleData[j].SubjectId != 0)
break;
}
if (rootAdded)
{
data.Add(mydataObject);
}

At least one object must implement IComparable error in linq query

var pairs = new [] { new { id = 1, name = "ram", dept = "IT", sal = "3000" }, new { id = 2, name = "ramesh", dept = "IT", sal = "5000" }, new { id = 3, name = "rahesh", dept = "NONIT", sal = "2000" },
new { id = 5, name = "rash", dept = "NONIT", sal = "7000" } };
var query = from stud in pairs
where (stud.name.StartsWith("r") && stud.id % 2 != 0)
//orderby stud.sal descending
group stud by stud.dept into grps
select new { Values = grps, Key = grps.Key, maxsal=grps.Max() };
////select new { id = stud.id };
foreach (dynamic result in query)
{
Console.WriteLine(result.Key);
Console.WriteLine(result.maxsal);
foreach (dynamic result2 in result.Values)
{
Console.WriteLine(result2.id + "," + result2.sal);
}
}
Console.Read();
I am getting the error "At least one object must implement IComparable.", can someone explain me why iam I getting this error ?
You are calling grps.Max() to get maximnum item in group. Your anonymous objects are not comparable. How Linq will know which one is maximum from them? Should it use id property for comparison, or name?
I believe you want to select max salary:
maxsal = grps.Max(s => Int32.Parse(s.sal))

Categories