How can I do this with a LINQ query? - c#

What I'm trying to do is take an IEnumerable of an object that has 2 fields and find how many of one of the fields is associated with only 1 of the first.
In other words, the setup is like
using System;
using System.Linq;
using System.Collections.Generic;
public class Baz
{
public string Foo { get; set; }
public int Bar { get; set; }
}
public class Program
{
public void Main()
{
List<Baz> bazzers = new List<Baz>() {
new Baz { Foo = "A", Bar = 1 },
new Baz { Foo = "A", Bar = 3 },
new Baz { Foo = "B", Bar = 1 },
new Baz { Foo = "B", Bar = 1 },
new Baz { Foo = "C", Bar = 2 },
new Baz { Foo = "C", Bar = 2 },
new Baz { Foo = "D", Bar = 1 },
new Baz { Foo = "D", Bar = 1 }
};
// WANTED: An IEnumerable<Baz> that is like
// { Bar = 1, LoyalFoos = 2 }, (because both "B" and "D" are paired only with 1)
// { Bar = 2, LoyalFoos = 1 }, (because "C" is paired only with 2)
// { Bar = 3, LoyalFoos = 0 } (because there is no Foo that is paired only with the Bar 3)
}
}
Is there a nice way to do this with LINQ?

I'm not exactly sure what you want for output. Maybe something like this if you're looking for the count of loyal foos for each bar:
var result = bazzers
.Select(bazzer => bazzer.Bar)
.Distinct()
.Select(bar => new
{
Bar = bar,
LoyalFoos = bazzers
.GroupBy(bazzer => bazzer.Foo)
.Count(group => group.All(bazzer => bazzer.Bar == bar))
});
Or if you want a grouping of each loyal foo given bar:
var result = bazzers
.Select(bazzer => bazzer.Bar)
.Distinct()
.Select(bar => new
{
Bar = bar,
LoyalFoos = bazzers
.GroupBy(bazzer => bazzer.Foo)
.Where(group => group.All(bazzer => bazzer.Bar == bar))
.SelectMany(group => group)
});

Related

How to search in C# LINQ

I have a string search query which I have from the frontend app but I have a problem with the query.
I have a list of objects which have Id (number = int).
If the user will write in the search box number 12(string) he should have all lists of objects which contains the number 12 in id.
Objects (1,8,80,90,180);
Another example is if the user will input the number 8. He should have output 8,80,180;
How to write linq for questions about such a thing?
Any example remember search query is a string and id is a number :(
using System;
using System.Linq;
public class Program
{
public class MyObject
{
public int Id { get; set; }
}
public static void Main()
{
var arr = new MyObject[]
{
new MyObject() { Id = 1 },
new MyObject() { Id = 8 },
new MyObject() { Id = 80 },
new MyObject() { Id = 90 },
new MyObject() { Id = 180 }
};
var searchQuery = "8";
var result = arr.Where(x => x.Id.ToString()
.Contains(searchQuery))
.Select(x => x.Id)
.ToList();
Console.WriteLine(String.Join(",", result));
}
}
https://dotnetfiddle.net/AiIdg2
Sounds like you want something like this
var input = "8";
var integers = new[] { 8, 80, 810, 70 };
var result = integers.Where(x => x.ToString().Contains(input));
Something like this could be enough:
using System.Globalization;
namespace ConsoleApp2
{
internal class Program
{
static void Main(string[] args)
{
var items = new[]
{
new Item { Id = 8 },
new Item { Id = 18 },
new Item { Id = 80 },
new Item { Id = 6 },
new Item { Id = 13 },
};
var itemsWithSearchString = items
.Select(x => new { Item = x, SearchString = x.Id.ToString(CultureInfo.InvariantCulture) })
.ToArray();
const string userInput = "8";
var matchingItems = itemsWithSearchString
.Where(x => x.SearchString.Contains(userInput, StringComparison.Ordinal))
.Select(x => x.Item)
.ToArray();
foreach (var item in matchingItems)
{
Console.WriteLine($"Matching item: {item}");
}
}
}
public class Item
{
public int Id { get; set; }
public override string ToString()
{
return $"Id {this.Id}";
}
}
}

Can I use LINQ GroupBy to do this more cleanly?

In this contrived example, which closely resembles my real-world problem, I have a data set coming from an external source. Each record from the external source takes the following form:
[Classification] NVARCHAR(32),
[Rank] INT,
[Data] NVARCHAR(1024)
I am looking to build an object where the Rank and Data are patched into a single instance of a response object that contains list properties for the three hard-coded Classification values, ordered by Rank.
I have something that works, but I can't help but think that it could be done better. This is what I have:
public static void Main()
{
IEnumerable<GroupingTestRecord> records = new List<GroupingTestRecord>
{
new GroupingTestRecord { Classification = "A", Rank = 1, Data = "A1" },
new GroupingTestRecord { Classification = "A", Rank = 2, Data = "A2" },
new GroupingTestRecord { Classification = "A", Rank = 3, Data = "A3" },
new GroupingTestRecord { Classification = "B", Rank = 1, Data = "B1" },
new GroupingTestRecord { Classification = "B", Rank = 2, Data = "B2" },
new GroupingTestRecord { Classification = "B", Rank = 3, Data = "B3" },
new GroupingTestRecord { Classification = "C", Rank = 1, Data = "C1" },
new GroupingTestRecord { Classification = "C", Rank = 2, Data = "C2" },
new GroupingTestRecord { Classification = "C", Rank = 3, Data = "C3" },
};
GroupTestResult r = new GroupTestResult
{
A = records.Where(i => i.Classification == "A").Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank),
B = records.Where(i => i.Classification == "B").Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank),
C = records.Where(i => i.Classification == "C").Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank),
};
The source record DTO:
public class GroupingTestRecord
{
public string Classification { get; set; }
public int? Rank { get; set; }
public string Data { get; set; }
}
The destination single class:
public class GroupTestResult
{
public IEnumerable<GroupTestResultItem> A { get; set; }
public IEnumerable<GroupTestResultItem> B { get; set; }
public IEnumerable<GroupTestResultItem> C { get; set; }
}
The distination child class:
public class GroupTestResultItem
{
public int? Rank { get; set; }
public string Data { get; set; }
}
Ouput
{
"A":[
{
"Rank":1,
"Data":"A1"
},
{
"Rank":2,
"Data":"A2"
},
{
"Rank":3,
"Data":"A3"
}
],
"B":[
{
"Rank":1,
"Data":"B1"
},
{
"Rank":2,
"Data":"B2"
},
{
"Rank":3,
"Data":"B3"
}
],
"C":[
{
"Rank":1,
"Data":"C1"
},
{
"Rank":2,
"Data":"C2"
},
{
"Rank":3,
"Data":"C3"
}
]
}
Fiddle
Is there a better way to achieve my goal here?
The same JSON output was achieved using GroupBy first on the Classification and applying ToDictionary on the resulting IGrouping<string, GroupingTestRecord>.Key
var r = records
.GroupBy(_ => _.Classification)
.ToDictionary(
k => k.Key,
v => v.Select(j => new GroupTestResultItem { Rank = j.Rank, Data = j.Data, }).OrderBy(k => k.Rank).ToArray()
);
var json = JsonConvert.SerializeObject(r);
Console.WriteLine(json);
which should easily deserialize to the destination single class (for example on a client)
var result = JsonConvert.DeserializeObject<GroupTestResult>(json);
is it possible to get the top level result into a GroupTestResult object?
Build the result from the dictionary
var result = new GroupTestResult {
A = r.ContainsKey("A") ? r["A"] : Enumerable.Empty<GroupTestResultItem>();,
B = r.ContainsKey("B") ? r["B"] : Enumerable.Empty<GroupTestResultItem>();,
C = r.ContainsKey("C") ? r["C"] : Enumerable.Empty<GroupTestResultItem>();,
};
Or this
var result = records.GroupBy(x => x.Classification)
.ToDictionary(x => x.Key, x => x.Select(y => new {y.Rank, y.Data})
.OrderBy(y => y.Rank));
Console.WriteLine(JsonConvert.SerializeObject(result));
Full Demo Here

What does a GroupBy KeySelector function look like?

I would like to break the KeySelector function of GroupBy into its own method. The KeySelector code isn't right. One major problem is Widget.foo isn't matching on "Green" but I think it should be.
Widget.cs
public class Widget
{
public string foo { get; set; }
public double bar { get; set; }
public bool fee { get; set; }
}
Program.cs
static void Main(string[] args)
{
var widgets = new List<Widget>()
{
new Widget() { foo = "red" , bar = 1.0, fee = true },
new Widget() { foo = "green", bar = 2.0, fee = true },
new Widget() { foo = "green", bar = 2.0, fee = false },
new Widget() { foo = "green", bar = 3.0, fee = false },
new Widget() { foo = "blue" , bar = 4.0, fee = true }
};
var gb = widgets.GroupBy(
w => GenerateGroupByKey(),
w => w,
(prop, groupedWidgets) => new
{
GroupedWidgets = groupedWidgets
}
).ToList();
}
KeySelector
static Func<Widget, object> GenerateGroupByKey()
{
Func<Widget, object> s = delegate(Widget widget)
{
return new { widget.foo };
};
return s;
}
You must pass delegate to parameters, so just call the function and return the delegate.
var gb = widgets.GroupBy(
GenerateGroupByKey(),
w => w,
(prop, groupedWidgets) => new
{
GroupedWidgets = groupedWidgets
}
).ToList();
Group by you using have parameters like below
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector,
Func<TKey, IEnumerable<TElement>, TResult> resultSelector
when you are writing w => GenerateGroupByKey()
you are creating a new FUNC with input parameter as GenerateGroupByKey()
But GenerateGroupByKey() itself is a FUNC
static Func<Widget, object> GenerateGroupByKey()
{
Func<Widget, object> s = delegate(Widget widget)
{
return new { widget.foo };
};
return s;
}
And you are not invoking that FUNC
Solution:
Do not create another FUNC. Pass GenerateGroupByKey() directly :)
static void Main(string[] args)
{
var widgets = new List<Widget>()
{
new Widget() { foo = "red" , bar = 1.0, fee = true },
new Widget() { foo = "green", bar = 2.0, fee = true },
new Widget() { foo = "green", bar = 2.0, fee = false },
new Widget() { foo = "green", bar = 3.0, fee = false },
new Widget() { foo = "blue" , bar = 4.0, fee = true }
};
var gb = widgets.GroupBy(
GenerateGroupByKey(), // Your FUNC method
w => w,
(prop, groupedWidgets) => new
{
GroupedWidgets = groupedWidgets
}
).ToList();
}
Your grouping does not work because inside GenerateGroupByKey() method you create new object.
Objects are equals if they have equal memory references. You can review more details about Equals here.
For what you create new object like new { widget.foo }?
You can just return string instead of object.
Try to use delegate like it:
static Func<Widget, object> GenerateGroupByKey()
{
Func<Widget, object> s = delegate(Widget widget)
{
return widget.foo;
};
return s;
}

C# linq totalization

I need to get a totalization by enum values. See this example:
In this source:
namespace ConsoleApplication
{
enum fruits { Orange, Grape, Papaya }
class item
{
public fruits fruit;
public string foo;
}
class Program
{
static void Main(string[] args)
{
item[] list = new item[]
{
new item() { fruit = fruits.Orange, foo = "afc" },
new item() { fruit = fruits.Orange, foo = "dsf" },
new item() { fruit = fruits.Orange, foo = "gsi" },
new item() { fruit = fruits.Orange, foo = "jskl" },
new item() { fruit = fruits.Grape, foo = "mno" },
new item() { fruit = fruits.Grape, foo = "pqu" },
new item() { fruit = fruits.Grape, foo = "tvs" },
};
var vTotals = from... //Here
}
}
}
I would like to vTotals be
of type
Dictionary<fruits, int>
with the values
{
{ fruits.Orange, 4 }
{ fruits.Grape, 3 }
{ fruits.Papaya, 0 }
}
How I can do it with Linq?
Thanks in advance
What you want to do here is logically a group join. You want to join this table with a sequence representing each of the fruits, and then count the size of those groups.
var totals = Enum.GetValues(typeof(fruits)).OfType<fruits>()
.GroupJoin(list,
fruit => fruit,
item => item.fruit,
(fruit, group) => new { Key = fruit, Value = group.Count() })
.ToDictionary(pair => pair.Key, pair => pair.Value);
You can use group join of all fruit types with items:
var vTotals = from fruits f in Enum.GetValues(typeof(fruits))
join i in list on f equals i.fruit into g
select new {
Fruit = f,
Count = g.Count()
};
Result:
[
{ Fruit: "Orange", Count: 4 },
{ Fruit: "Grape", Count: 3 },
{ Fruit: "Papaya", Count: 0 }
]
Here's one way to do it. It may not be considered as pretty as doing it all in one query, but it is (IMO) clear: (first part of the code thanks to D Stanley, who since deleted his answer)
var vTotals = list.GroupBy(i => i.fruit)
.ToDictionary(g => g.Key, g => g.Count());
foreach (var fruit in Enum.GetValues(typeof(fruits)).Cast<fruits>()
.Where(x => !vTotals.ContainsKey(x)))
{
vTotals.Add(fruit, 0);
}
var total = from e in Enum.GetValues(typeof(fruits)).OfType<fruits>()
select new
{
Fruit = e,
Count = list.Where(f => f.fruit == e).Count()
};
From #Servy's answer, if you want to unit test it, using MSTest.
[TestClass]
public class DummyTests {
[TestMethod]
public void GroupCountByFruitType() {
// arrange
var expected = new Dictionary<Fruits, int>() {
{ Fruits.Grape, 3 }
, { Fruits.Orange, 4 }
, { Fruits.Papaya, 0 }
};
Item[] list = new Item[] {
new Item() { Fruit = Fruits.Orange, Foo = "afc" },
new Item() { Fruit = Fruits.Orange, Foo = "dsf" },
new Item() { Fruit = Fruits.Orange, Foo = "gsi" },
new Item() { Fruit = Fruits.Orange, Foo = "jskl" },
new Item() { Fruit = Fruits.Grape, Foo = "mno" },
new Item() { Fruit = Fruits.Grape, Foo = "pqu" },
new Item() { Fruit = Fruits.Grape, Foo = "tvs" }
};
// act
var actual = Enum.GetValues(typeof(Fruits)).OfType<Fruits>()
.GroupJoin(list
, fruit => fruit
, item => item.Fruit
, (fruit, group) => new { Key = fruit, Value = group.Count() })
.ToDictionary(pair => pair.Key, pair => pair.Value);
// assert
actual.ToList()
.ForEach(item => Assert.AreEqual(expected[item.Key], item.Value));
}
private class Item {
public Fruits Fruit { get; set; }
public string Foo { get; set; }
}
private enum Fruits {
Grape,
Orange,
Papaya
}
}
This should do it
var vTotals = list.GroupBy(item => item.fruit)
.Select(item => Tuple.Create(item.Key, item.Count()))
.ToDictionary(key => key.Item1, value => value.Item2);
Here we simply group on the fruit name with their count and later turn it into a dictionary

How to Translate SQL "WHERE expr IN (query)" into LINQ?

Basically I want to make this SQL query with linq:
SELECT *
FROM Orders
WHERE Identifier IN (SELECT DISTINCT [Order] FROM OrderRows WHERE Quantity = '1')
This is what I have come up with:
var q = from o in db.Orders
where o.Identifier in (from r in db.OrderRows
where r.Quantity == 1 select r.Order).Distinct());
But the in after o.Identifier is not valid.
What is the correct syntax for the keyword IN?
I'm a little late, but I made a demo!
As other people have stated, I always use Contains:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ContainsExample
{
class Program
{
static void Main(string[] args)
{
var foos = new List<Foo>
{
new Foo { ID = 1, FooName = "Light Side" },
new Foo { ID = 2, FooName = "Dark Side" }
};
var bars = new List<Bar>
{
new Bar { ID = 1, BarName = "Luke", FooID = 1 },
new Bar { ID = 2, BarName = "Han", FooID = 1 },
new Bar { ID = 3, BarName = "Obi-Wan", FooID = 1 },
new Bar { ID = 4, BarName = "Vader", FooID = 2 },
new Bar { ID = 5, BarName = "Palpatine", FooID = 2 },
new Bar { ID = 6, BarName = "Fett", FooID = 2 },
new Bar { ID = 7, BarName = "JarJar", FooID = 3 }
};
var criteria = from f in foos
select f.ID;
var query = from b in bars
where criteria.Contains(b.FooID)
select b;
foreach (Bar b in query)
{
Console.WriteLine(b.BarName);
}
Console.WriteLine();
Console.WriteLine("There should be no JarJar...");
Console.ReadLine();
}
}
public class Foo
{
public int ID { get; set; }
public string FooName { get; set; }
}
public class Bar
{
public int ID { get; set; }
public string BarName { get; set; }
public int FooID { get; set; }
}
}
from o in db.Orders
where o.Identifier.Any
(
from r in db.OrderRows
where r.Quantity == 1
select r.Order
).Distinct()
select o
Try this...
It seems like you want a join:
var q = (from o in db.Orders
join r in db.OrderRows on o.Identifier equals r.Order
where r.Quantity == 1
select o).Distinct();
var q = from o in db.Orders
where (from r in db.OrderRows
where r.Quantity == 1 select r.Order).Distinct().Contains(o.Identifier);
The short answer is you want to take advantage of the Contains method.
int[] ids = { 2, 5, 6, 1 };
var a = from myRecords in context.db
where ids.Contains (myRecords.id)
select new {Id = myRecords.id};
Filtering on two sets of results works the same way, in that you can filter on any common property shared by the two sets:
string[] cities = { "London", "Paris", "Seattle" };
var query = dataContext.Customers.Where (c => cities.Contains (c.City));
have you tried using something like this:
int[] inKeyword = { 5, 7, 9 };
var q = from o in db.Orders.Where(p => inKeyword.Contains(p.Identifier));
Hope it helps :)

Categories