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}";
}
}
}
Related
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
If I have objects, lets call them Group that has list of some other objects I will call it Brand, and this object has a list of objects called Model.
Is there a way to get only list of Models using MongoDb c# driver.
I tried using SelectMany multiple times but with no success. If I put more than one SelectMany I always get an empty list.
Code should be self-explanatory.
At the end is comment that explains what confuses me.
class Group
{
[BsonId(IdGenerator = typeof(GuidGenerator))]
public Guid ID { get; set; }
public string Name { get; set; }
public List<Brand> Brands { get; set; }
}
class Brand
{
public string Name { get; set; }
public List<Model> Models { get; set; }
}
class Model
{
public string Name { get; set; }
public int Produced { get; set; }
}
class Program
{
static void Main(string[] args)
{
MongoClient client = new MongoClient("mongodb://127.0.0.1:32768");
var db = client.GetDatabase("test");
var collection = db.GetCollection<Group>("groups");
var fca = new Group { Name = "FCA" };
var alfaRomeo = new Brand { Name = "Alfra Romeo" };
var jeep = new Brand { Name = "Jeep" };
var ferrari = new Brand { Name = "Ferrari"};
var laFerrari = new Model { Name = "LaFerrari", Produced = 4 };
var wrangler = new Model { Name = "Wrangler", Produced = 3 };
var compass = new Model { Name = "Compass", Produced = 8 };
var giulietta = new Model { Name = "Giulietta", Produced = 7 };
var giulia = new Model { Name = "Giulia", Produced = 8 };
var _4c = new Model { Name = "4C", Produced = 6 };
fca.Brands = new List<Brand> { ferrari, alfaRomeo, jeep };
ferrari.Models = new List<Model> { laFerrari };
jeep.Models = new List<Model> { wrangler, compass };
alfaRomeo.Models = new List<Model> { giulietta, giulia, _4c };
collection.InsertOne(fca);
Console.WriteLine("press enter to continue");
Console.ReadLine();
var models = collection.AsQueryable().SelectMany(g => g.Brands).SelectMany(b => b.Models).ToList();
Console.WriteLine(models.Count); //returns 0 I expected 6
Console.ReadLine();
}
}
Try
var models = collection.AsQueryable()
.SelectMany(g => g.Brands)
.Select(y => y.Models)
.SelectMany(x=> x);
Console.WriteLine(models.Count());
Working output (with extra Select())
aggregate([{
"$unwind": "$Brands"
}, {
"$project": {
"Brands": "$Brands",
"_id": 0
}
}, {
"$project": {
"Models": "$Brands.Models",
"_id": 0
}
}, {
"$unwind": "$Models"
}, {
"$project": {
"Models": "$Models",
"_id": 0
}
}])
OP Output without extra Select()
aggregate([{
"$unwind": "$Brands"
}, {
"$project": {
"Brands": "$Brands",
"_id": 0
}
}, {
"$unwind": "$Models"
}, {
"$project": {
"Models": "$Models",
"_id": 0
}
}])
I have a list in this table
public class Fruits
{
public int ID { get; set; }
public string Name{ get; set; }
}
I want to know what are the most frequent fruit in this table what is the code that appears to me this result
I am use
var max = db.Fruits.Max();
There is an error in that?
Try
public class Fruits
{
public int ID { get; set; }
public string Name{ get; set; }
}
var Val = fruitList.GroupBy(x => x.ID,
(key, y) => y.MaxBy(x => x.ID).value)
As Drew said in the comments, you want to GroupBy on the value that you care about (I did Name, since ID tends to be unique in most data structures) and then OrderByDescending based on the count.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var fruits = new List<Fruit> { new Fruit { ID = 1, Name = "Apple" }, new Fruit { ID = 2, Name = "Apple" }, new Fruit { ID = 3, Name = "Pear" } };
var most = fruits.GroupBy(f => f.Name).OrderByDescending(group => group.Count());
Console.WriteLine(most.First().Key);
}
}
public class Fruit
{
public int ID { get; set; }
public string Name{ get; set; }
}
If you want to get the name of the item that exists most in your list, first find the id that is most occurring:
var fruitAnon = fruits
.GroupBy(item => item.ID)
.Select(item => new {
Key = item.Key,
Count = item.Count()
})
.OrderByDescending(item => item.Count)
.FirstOrDefault();
This will return an anonymous object that will have the most frequent id, and the count represents the number of times it exists in the list. You can then find that object's name:
var fruit = fruits.FirstOrDefault(x => x.ID == fruitAnon.Key);
If you had a list like this:
List<Fruits> fruits = new List<Fruits>() {
new Fruits { ID = 1, Name = "Apple" },
new Fruits { ID = 1, Name = "Apple" },
new Fruits { ID = 2, Name = "Orange" },
new Fruits { ID = 2, Name = "Orange" },
new Fruits { ID = 2, Name = "Orange" },
new Fruits { ID = 2, Name = "Orange" }
};
Then:
Console.WriteLine(fruit.Name);
Would print Orange.
I have a number of objects and each object has an array, I would like to group these objects by the values inside the array, so conceptually they look as follows:
var objects = new []{
object1 = new object{
elements = []{1,2,3}
},
object2 = new object{
elements = []{1,2}
},
object3 = new object{
elements = []{1,2}
},
object4 = new object{
elements = null
}
}
after grouping:
group1: object1
group2: object2,object3
group3: object4
somethings that I have tried:
actual classes:
public class RuleCms
{
public IList<int> ParkingEntitlementTypeIds { get; set; }
}
var rules = new List<RuleCms>()
{
new RuleCms()
{
ParkingEntitlementTypeIds = new []{1,2}
},
new RuleCms()
{
ParkingEntitlementTypeIds = new []{1,2}
},
new RuleCms()
{
ParkingEntitlementTypeIds = new []{1}
},
new RuleCms()
{
ParkingEntitlementTypeIds = null
}
};
var firstTry = rules.GroupBy(g => new { entitlementIds = g.ParkingEntitlementTypeIds, rules = g })
.Where(x => x.Key.entitlementIds !=null && x.Key.entitlementIds.Equals(x.Key.rules.ParkingEntitlementTypeIds));
var secondTry =
rules.GroupBy(g => new { entitlementIds = g.ParkingEntitlementTypeIds ?? new List<int>(), rules = g })
.GroupBy(x => !x.Key.entitlementIds.Except(x.Key.rules.ParkingEntitlementTypeIds ?? new List<int>()).Any());
You can use IEqualityComparer class. Here is the code:
class MyClass
{
public string Name { get; set; }
public int[] Array { get; set; }
}
class ArrayComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] x, int[] y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(int[] obj)
{
return string.Join(",", obj).GetHashCode();
}
}
Then
var temp = new MyClass[]
{
new MyClass { Name = "object1", Array = new int[] { 1, 2, 3 } },
new MyClass { Name = "object2", Array = new int[] { 1, 2 } },
new MyClass { Name = "object3", Array = new int[] { 1, 2 } },
new MyClass { Name = "object4", Array =null }
};
var result = temp.GroupBy(i => i.Array, new ArrayComparer()).ToList();
//Now you have 3 groups
For simple data that really is as simple as your example you could do this:
.GroupBy(x => string.Join("|", x.IDS))
.Select(x => new
{
IDS = x.Key.Split('|').Where(s => s != string.Empty).ToArray(),
Count = x.Count()
});
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