linq to objects query: select objects where no other values exist - linq

Help with the linq query to select the object where each date only has item of one value.
So in the following list:
1) 2011-1-4 would be selected because its items are both 25
2) 2011-1-1 would not be selected because it has items 22 and 25
3) 2011-1-2 and 2011-1-3 would be selected because they only have one item.
public class MyClass
{
public DateTime date { get; set; }
public int item { get; set; }
}
static void Main(string[] args)
{
List<MyClass> classes = new List<MyClass>
{
{ new MyClass() { date = new DateTime(2011, 1, 1), item = 22 } },
{ new MyClass() { date = new DateTime(2011, 1, 1), item = 25 } },
{ new MyClass() { date = new DateTime(2011, 1, 2), item = 23 } },
{ new MyClass() { date = new DateTime(2011, 1, 3), item = 24 } },
{ new MyClass() { date = new DateTime(2011, 1, 4), item = 25 } },
{ new MyClass() { date = new DateTime(2011, 1, 4), item = 25 } },
};
}
What about this? Pretty much got from looking at your guys' answers.
var results = classes.GroupBy(m => m.date);
var q = from n in results where n.All( r => r.item == n.First().item) select n;

This is some unedited ugliness that I've run through Linqpad that seems to meet your requirement.
var query = classes.GroupBy(c => c.date)
.Select(g => g.GroupBy(c => c.item))
.Where(sg => sg.Count() == 1)
.SelectMany(sg => sg)
.SelectMany(g => g)
.Select(c => c.date)
.Distinct();
Returns the dates 1/2, 1/3, and 1/4 as per your requirement.

This seems to work, but I wouldn't say I'm happy with it:
var results = from m in classes
group m by new { m.date } into x
where x.GroupBy(y => y.item).Count() == 1
select x;

var results = classes
.GroupBy(m => m.date)
.Where(g => g.Select(x => x.item).Distinct().Count() == 1)
.Select(g => g.Key).ToList();

Related

NEST API Default value for GeoShapes fields

We are using a filter as per following:
filters.Add(fq => fq
.Term(t => t
.Field(f => f.LocalityId)
.Value(locationParams[2])) || fq
.GeoShape(g => g
.Field("locationShape")
.Relation(GeoShapeRelation.Within)
.IndexedShape(f => f
.Id(searchCriteria.spLocationId)
.Index(indexName)
.Path("geometry")
)
)
);
However, if the geometry field is missing, Elasticsearch throws an exception.
Is there anyway to avoid this by using a default (Null Value) in the mapping or any other way.
It is not possible to avoid the exception in this case. Elasticsearch assumes that the parameters that the user provides to a pre-indexed shape are valid.
Ideally, the values supplied to the indexed shape should be constrained in a manner that prevents an end user from supplying invalid values. If that is unfeasible, you could run a bool query with filter clauses of exists query and ids query on the indexName index before adding the indexed shape geoshape query filter to the search request.
For example
private static void Main()
{
var documentsIndex = "documents";
var shapesIndex = "shapes";
var host = "localhost";
var pool = new SingleNodeConnectionPool(new Uri($"http://{host}:9200"));
var settings = new ConnectionSettings(pool)
.DefaultMappingFor<Document>(m => m.IndexName(documentsIndex))
.DefaultMappingFor<Shape>(m => m.IndexName(shapesIndex));
var client = new ElasticClient(settings);
if (client.Indices.Exists(documentsIndex).Exists)
client.Indices.Delete(documentsIndex);
client.Indices.Create(documentsIndex, c => c
.Map<Document>(m => m
.AutoMap()
)
);
if (client.Indices.Exists(shapesIndex).Exists)
client.Indices.Delete(shapesIndex);
client.Indices.Create(shapesIndex, c => c
.Map<Shape>(m => m
.AutoMap()
)
);
client.Bulk(b => b
.IndexMany(new [] {
new Document
{
Id = 1,
LocalityId = 1,
LocationShape = GeoWKTReader.Read("POLYGON ((30 20, 20 15, 20 25, 30 20))")
},
new Document
{
Id = 2,
LocalityId = 2
},
})
.IndexMany(new []
{
new Shape
{
Id = 1,
Geometry = GeoWKTReader.Read("POLYGON ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35))")
}
})
.Refresh(Refresh.WaitFor)
);
var shapeId = 1;
var searchResponse = client.Search<Shape>(s => s
.Size(0)
.Query(q => +q
.Ids(i => i.Values(shapeId)) && +q
.Exists(e => e.Field("geometry"))
)
);
Func<QueryContainerDescriptor<Document>, QueryContainer> geoShapeQuery = q => q;
if (searchResponse.Total == 1)
geoShapeQuery = q => +q
.GeoShape(g => g
.Field("locationShape")
.Relation(GeoShapeRelation.Within)
.IndexedShape(f => f
.Id(shapeId)
.Index(shapesIndex)
.Path("geometry")
)
);
client.Search<Document>(s => s
.Query(q => +q
.Term(t => t
.Field(f => f.LocalityId)
.Value(2)
) || geoShapeQuery(q)
)
);
}
public class Document
{
public int Id { get; set; }
public int LocalityId { get; set; }
public IGeoShape LocationShape { get; set; }
}
public class Shape
{
public int Id { get; set; }
public IGeoShape Geometry { get; set; }
}
If var shapeId = 1; is changed to var shapeId = 2; then the geoshape query is not added to the filter clauses when searching on the documents index.

LINQ - Group By child property

I have a list of users, each user has string array property called Tags. I am trying to get a unique list of tags and a total count, any idea what I a missing here? I am using LinqPad to write my test query, please see the example code:
void Main()
{
List<User> users = new List<User>(){
new User {Id = 1, Tags = new string[]{"tag1", "tag2"}},
new User {Id = 2, Tags = new string[]{"tag3", "tag7"}},
new User {Id = 3, Tags = new string[]{"tag7", "tag8"}},
new User {Id = 4, Tags = new string[]{"tag1", "tag4"}},
new User {Id = 5 },
};
var uniqueTags = users.Where(m=>m.Tags != null).GroupBy(m=>m.Tags).Select(m=> new{TagName = m.Key, Count = m.Count()});
uniqueTags.Dump();
// RESULT should BE:
// tag1 - Count(2)
// tag2 - Count(1)
// tag3 - Count(1)
// tag4 - Count(1)
// tag7 - Count(2)
// tag8 - Count(1)
}
public class User{
public int Id {get;set;}
public string[] Tags {get;set;}
}
You can flatten to IEnumerable<string> before grouping:
var uniqueTags = users.SelectMany(u => u.Tags ?? new string[0])
.GroupBy(t => t)
.Select(g => new { TagName = g.Key, Count = g.Count() } );
LINQPad C# Expression version:
new[] {
new { Id = 1, Tags = new[] { "tag1", "tag2" } },
new { Id = 2, Tags = new[] { "tag3", "tag7" } },
new { Id = 3, Tags = new[] { "tag7", "tag8" } },
new { Id = 4, Tags = new[] { "tag1", "tag4" } },
new { Id = 5, Tags = (string[])null }
}
.SelectMany(u => u.Tags ?? Enumerable.Empty<string>())
.GroupBy(t => t)
.Select(g => new { TagName = g.Key, Count = g.Count() } )

Dynamic where clause with two entity

I need a filter between two entity.
Have two tables 1.User 2.Product
Product map with the User table.
I am going to create a dynamic where filter.
I need to find out all the users which have 'test' product.
Conditions: if userFilter count is 0 then I need all test product with the respected user.
If userFilter is there and productFilter is there then below code is working but if userFilter is not there and productFilter is there then it returning 0 row. How can I find the users which have test product? ?
Here is my Code.
public IHttpActionResult GetFilter()
{
var userFilters = new List<Filter>()
{
new Filter { PropertyName = "Username" ,
Operation = Op .Equals, Value = "Karan" },
};
var productfilter = new List<Filter>()
{
new Filter { PropertyName = "Name" ,
Operation = Op .Equals, Value = "Test product" }
};
Func<User, bool> deleg = x => true;
Func<Product, bool> delegProduct = x => true;
if (userFilters.Count > 0)
{
deleg = ExpressionBuilder.GetExpression<User>(userFilters).Compile();
}
if (productfilter.Count > 0)
{
delegProduct = ExpressionBuilder.GetExpression<Product>(productfilter).Compile();
}
var resultt = _localmarketEntities.Users.Where(deleg)
.Select(x => new
{
x.Id,
x.Username,
Product = x.Products.Where(delegProduct).Select(y => new
{
y.Id,
y.Name
}).ToList()
})
.ToList();
return Ok(resultt);
}
if i understand correct, you need all users that have test product when userFiler count is 0.
List<User> res;
var user = _localmarketEntities.Users.Where(deleg).ToList();
if (user.Count == 0) {
res = _localmarketEntities.Products.Where(delegProduct).Select(q => new User() {
Id = q.Id,
Username = q.Username,
Product = q
}).ToList();
}
else {
res = _localmarketEntities.Users.Where(deleg)
.Select(x => new
{
x.Id,
x.Username,
Product = x.Products.Where(delegProduct).Select(y => new
{
y.Id,
y.Name
}).ToList()
})
.ToList();
}

Getting all elements concatenate in a string with LINQ

I have the following classes:
public class People
{
public int ID { get; set; }
public List<Right> Rights { get; set; }
}
public class Right
{
public int ID { get; set; }
public int Val { get; set; }
}
With the following values:
People: ID: 1
Right:
ID: 1
Val: 5
Right:
ID: 2
Val: 4
I would like to retrieve all Val rights (in a single string) for the people.
So for people with ID 1 : getting "5,4".
List<People> list = ...;
string result = string.Join(",", (people.First(p => p.ID == 1)).Rights.Select(r => r.Val));
Example :
List<People> people = new List<People>()
{
new People()
{
ID = 1, Rights = new List<Right>()
{
new Right() { ID = 1, Val = 5 },
new Right() { ID = 2, Val = 10 },
}
},
new People()
{
ID = 2, Rights = new List<Right>()
{
new Right() { ID = 1, Val = 6 },
new Right() { ID = 2, Val = 11 },
}
}
};
string result = string.Join(",", (people.First(p => p.ID == 1)).Rights.Select(r => r.Val));
Console.WriteLine(result);
Output: 5, 10
You can use SelectMany to flatten a structure, so something like:
var vals = people.Where(p => p.ID == 1)
.SelectMany(p => p.Rights.Select(r => Val));
var str = String.Join(",", vals.Select(v => v.ToString());

LINQ RowNumber, Aggregate (Sum) and GroupBy

I have an SQL code like;
Select GroupName, sum(LineAmount) as Total, WeekNumber,
ROW_NUMBER() over (partition by WeekNumber order by sum(LineAmount) desc) as RowNum
from
Invoices
where
month(InvoiceDate)=month(getdate())
group by
GroupName,WeekNumber
I would like to convert this to LINQ, but no luck. I am using LINQ to Object. Any help would be appreciated.
TIA
EDIT : Here is some sample data, and the expected result.
public class Invoice
{
public string GroupName { get; set; }
public int LineAmount { get; set; }
public int WeekNum { get; set; }
}
List<Invoice> theData = new List<Invoice>();
theData.Add(new Invoice { GroupName = "A", LineAmount = 1, WeekNum = 1});
theData.Add(new Invoice { GroupName = "A", LineAmount = 2, WeekNum = 1 });
theData.Add(new Invoice { GroupName = "A", LineAmount = 3, WeekNum = 1 });
theData.Add(new Invoice { GroupName = "A", LineAmount = 2, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "A", LineAmount = 3, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "A", LineAmount = 4, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 4, WeekNum = 1 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 3, WeekNum = 1 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 7, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 6, WeekNum = 2 });
theData.Add(new Invoice { GroupName = "B", LineAmount = 5, WeekNum = 2 });
I have removed "where" from my first query as its not a problem at the moment.
theData
.GroupBy(g => new {g.GroupName, g.WeekNum}, (key, gg) => new {key.GroupName, key.WeekNum, Total = gg.Sum(g => g.LineAmount)})
.GroupBy(g => g.WeekNum, (weekNum, gg) => gg.OrderByDescending(g => g.Total).Select((g,i) => new {g.GroupName, g.Total, g.WeekNum, RowNum = i}))
.SelectMany(g => g)
You have not specified the language you need it in. Here is the code in C#
int index = 0;
var filteredInvoices = (from i in invoices
where i.InvoiceDate.Month == DateTime.Now().Month
group i by new { i.GroupName, i.WeekNumber }
into ig
select new {i.GroupName, Total = ig.Sum(i => i.LineAmount), i.WeekNumber, RowNum = ++index}).OrderByDescending(n => n.Total);
filteredInvoices should have the result that you want. Also I am assuming that the i.InvoiceDate is of type DateTime.
Serg Rogovtsev answer gives me expected result. And the below code is what I have done. Don't know which performs better, but results are same.
(theData.GroupBy(f => new { f.GroupName, f.WeekNum})
.Select(r => new {r.Key.WeekNum, r.Key.GroupName, Total = r.Sum(f => f.LineAmount)}))
.GroupBy(r => new {r.WeekNum}).SelectMany(
g =>
g.OrderByDescending(f => f.Total).Select(
(f, index) => new { f.GroupName, f.Total, f.WeekNum, Ix = index + 1 }))

Resources