just another tagcloud question - c#

i have an act:
public class Act
{
public Act()
{
this.Tags = new List<Tag>();
}
public string Id {get; set;}
public string Name { get; set; }
public IList<Tag> Tags { get; set; }
}
Tag is just:
public class Tag
{
public string Name { get; set; }
public string LfmUrl { get; set; }
}
I want to be able to query the DB and get a list of TagCount back which shows the name of the tag and the number of times it appears.
So far I have this:
public class Tags_ByTagCloud : AbstractIndexCreationTask
{
public override IndexDefinition CreateIndexDefinition()
{
return new IndexDefinition<Act, TagAndCount>
{
Map = docs => from doc in docs
from tag in doc.Tags
select new
{
Name = tag.Name,
Count = 1
},
Reduce = results => from result in results
group result by result.TagName
into g
select new
{
Name = g.Key,
Count = g.Sum(x => x.Count)
},
SortOptions = {{x => x.Count, SortOptions.Int}}
}.ToIndexDefinition(DocumentStore.Conventions);
}
}
Which happily outputs nothing. I know there are thousands of acts each with at least a few tags. Any ideas?
I ideally want to pass in a list of act ids to query this against. So, given a list of acts, what are the tags and how many times does
each occur?

Just had to change all TagName to match Name and it mapped nicely

Related

Linq method - Return all Recipes that contain all Tags selected

This query gets all the rows from the join table, TagRecipes (many-to-many), where the TagId is found in a list, tagIdList, and lastly just returns the Recipe. How can I make it so it only returns Recipes that have all the tags in the list, tagIdList?
Basically, it's filtering by 'or', but I want it to filter by 'and'. The recipe must contain ALL the tags, not just some.
var allRecipes = await _context.TagRecipes
.Where(tr => tagIdList.Contains(tr.TagId))
.Select(i => i.Recipe).Distinct()
.ToListAsync();
e.g, tagIdList = {17, 20 ,21 }
TagRecipes
So, it should only return Recipe with RecipeId = 2, even though RecipeID 4 contains TagId 17
Classes
public class Recipe
{
public int Id { get; set; }
public string Name { get; set; }
public string Ingredients { get; set; }
public string Instructions { get; set; }
public string ImageURL { get; set; }
public string Description { get; set; }
public ICollection<TagRecipe> TagRecipes { get; set; }
public virtual ICollection<StarRating> StarRatings { get; set; }
public ICollection<Binder> Binders { get; set; }
}
public class TagRecipe
{
public int TagId { get; set; }
public int RecipeId { get; set; }
public Tag Tag { get; set; }
public Recipe Recipe { get; set; }
}
Thank you
group TagRecipes by RecipId, so each RecipId (Key) has its own tagIds.
loop on each group to check if it has all the tags in the provided tagIdList, and if it has them all, store this RecipId (Key), in my case i created list of int RecipIds.
get all the Recipes in the RecipIds list.
I hope this could be helpful
List<int> RecipIds = new List<int>();
int count = 0;
var RecipGroup = _context.TagRecipes.GroupBy(tr => tr.RecipeId);
foreach (var group in RecipGroup)
{
count = 0;
foreach (var tr in group)
{
if (tagIdList.Contains(tr.TagId))
{
count += 1;
}
}
if (tagIdList.Length == count)
{
RecipIds.Add(group.Key);
}
}
var allRecipes = _context.Recipes.Where(r => RecipIds.Contains(r.Id)).ToList();
I believe the following solution using Linqkit will be the simplest way to solve this, and without returning duplicates.
var tagIdList = new List<int> {1, 2};
var predicate = tagIdList.Aggregate(PredicateBuilder.New<Recipe>(), (pred, currentTagId) =>
pred.And(recipe => recipe.TagRecipes.Any(x => x.TagId == currentTagId)));
var result = _context.Recipes.Where(predicate).ToList();
Generates this SQL:
SELECT "r"."Id", "r"."Name"
FROM "Recipes" AS "r"
WHERE EXISTS (
SELECT 1
FROM "TagRecipes" AS "t"
WHERE ("r"."Id" = "t"."RecipeId") AND ("t"."TagId" = #__currentTagId_0)) AND EXISTS (
SELECT 1
FROM "TagRecipes" AS "t0"
WHERE ("r"."Id" = "t0"."RecipeId") AND ("t0"."TagId" = #__currentTagId_1))
Code is tested and verified using an asp.net Core 5 app.
Basically, it's filtering by 'or', but I want it to filter by 'and'.
The recipe must contain ALL the tags, not just some.
So, it should only return Recipe with RecipeId = 2, even though RecipeID 4 contains
TagId 17
Since you want to filter the data by and, after filtering the data based on the tag, if group the result based on the RecipeId property, the item count in the group should be the equally with the tag list count. So, you could use the following query statement:
var tagIdList = new List<int>() { 17, 20, 21 };
var allRecipes = _context.TagRecipes.Include(tr => tr.Recipe)
.Where(tr => tagIdList.Contains(tr.TagId))
.ToList()
.GroupBy(tr=> tr.RecipeId)
.Where(c => c.Count() == tagIdList.Count)
.Select(i => new { RecipeId = i.Key, Count = i.Count(), Recipe = i.FirstOrDefault().Recipe })
.ToList();
The result as below:

Read CSV file to nested objects

I have CSV file like
Title|Column|Value
A|Z1|1
A|Z1|2
A|Z1|3
A|Z2|1
A|Z2|5
B|Z3|4
B|Z3|6
....
I want to read this csv file into the following class hierarchy:
(I want to end up with a List of MyClass)
class MyClass
{
public string Title { get; set; }
public List<Column> Columns { get; set; }
}
class Column
{
public string Column { get; set; }
public List<Value> Values { get; set; }
}
class Value
{
public string Value { get; set; }
}
I am not able to comprehend how to achieve this.
What I tried up till now is:
class DTO
{
public string Title { get; set; }
public string Column { get; set; }
public string Value { get; set; }
}
List<DTO> records = new List<DTO>();
using (var reader = new StreamReader(model.Path))
using (var csv = new CsvReader(reader))
{
records = csv.GetRecords<DTO>().ToList();
}
var temp = records
.GroupBy(x => x.Title)
.Select(y => new
{
Title = y.Key,
Columns = y.SelectMany(x => new { x.Column, x.Value }) //I am not sure how to proceed from here
});
The issue I am facing is how can I further group by on the columns and get the values, or if I should be using another approach?
Some more explanation on the csv structure:
There can be multiple Values for each column, and there can be multiple columns for each Title.
There can be multiple Values for each column, and there can be multiple columns for each Title.
That means that you have to repeat the first step and GroupBy again by columns. You also should use your types that you already implemented for this. Don't use anonymous types:
IEnumerable<MyClass> temp = records
.GroupBy(x => x.Title)
.Select(y => new MyClass
{
Title = y.Key,
Columns = y.GroupBy(x => x.Column)
.Select( c => new Column
{
Column_ = c.Key,
Values = c.Select(v => new Value
{
Value_ = v.Value
}).ToList()
}).ToList()
});
temp.Dump();
Result (from the LINQPad Dump):
PS. I changed the names of the properties, because the compiler does not allow them to be named exactly as the class is.
class Column
{
public string Column_ { get; set; }
public List<Value> Values { get; set; }
}
class Value
{
public string Value_ { get; set; }
}

Array inside list object, how to handle?

I have these two classes:
public class Order
{
public int ID { get; set; }
public int Output { get; set; }
public int Wharf { get; set; }
public int PartOf { get; set; }
public int[] Product { get; set; }
public int[] Quantity { get; set; }
public int[] Storage { get; set; }
public override bool Equals(Order obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// Return true if the fields match:
return (ID == obj.ID);
}
}
public class RawOrderData
{
public int ID { get; set; }
public int Output { get; set; }
public int Wharf { get; set; }
public int PartOfID { get; set; }
public int ProductID { get; set; }
public int Quantity { get; set; }
}
Every order in the system is in the form as class Order, the array is used when there are more than one product in the order.
RawOrderData is created from a JSON string where every product in the order have its own object. I want to create a List<Order> where every order gets its own object in the list so there not are several orders with same order id when order contains more than one product.
// raw data is here the JSON string
rawdatalist = serializer.Deserialize<List<RawOrderData>> (rawdata);
// Convert raw objects to List<Order>, list of orders
List<Order> orders = new List<Order> ();
orders = ConvertRawOrderToList (rawdatalist);
private List<Order> ConvertRawOrderToList(List<RawOrderData> datalist)
{
List<Order> orders = new List<Order> ();
foreach (RawOrderData dataobj in datalist)
{
// Check if order exists in list
if (orders.Contains(new Order () {ID = dataobj.ID}))
{
// Order exists, add more products
// CODE HERE?
} else {
// order not existing, add new order to list
short storage = GetStorageArea(dataobj.ProductID);
orders.Add (new Order () {ID = dataobj.ID, Output = dataobj.Output, Wharf = dataobj.Wharf, PartOf = dataobj.PartOfID, Product = dataobj.ProductID, Quantity = dataobj.Quantity});
}
}
return orders;
}
Do I think correct with the ConvertRawOrderToList method? The problem is I don't know what to write in // CODE HERE?. When there is array inside the list-object I'm confused.
I'm also wondering how to access all values in the List<Order> orders.
The information to Storage[] is created from another method that have product ID as input.
It sounds like you have a "flattened" collection of objects that you want to group into Orders. If that's the case, a basic Linq projection would be simplest:
var orders = datalist.GroupBy(o => o.ID)
.Select(g => new Order {
ID = g.Key,
Output = g.First().Output,
Wharf = g.First().Wharf,
PartOf = g.First().PartOf,
Product = g.Select(o => o.Product).ToArray(),
Quantity = g.Select(o => o.Product).ToArray(),
})
.ToList();
Then you don't need to worry about overriding Equals (at least not for this purpose).
Where would I add the method for adding Storage also?
Since your GetStorageArea function takes a single ProductID you need to pass the product IDs to that function:
var orders = datalist.GroupBy(o => o.ID)
.Select(g => new Order {
ID = g.Key,
Output = g.First().Output,
Wharf = g.First().Wharf,
PartOf = g.First().PartOf,
Product = g.Select(o => o.Product).ToArray(),
Quantity = g.Select(o => o.Product).ToArray(),
Storage = g.Select(o => GetStorageArea(o.Product)).ToArray()
})
.ToList();

Using LINQ How to access values of List item inside a Class

I have two classes MoviesListRootObject and Response. MoviesListRootObject conatins list of Response I want to access the id, Title and description that comes to List<Response> and assign that to var.
public class MoviesListRootObject
{
public int count { get; set; }
public Pagination pagination { get; set; }
public List<Response> response { get; set; }
}
[Serializable]
public class Response
{
public int id { get; set; }
public string title { get; set; }
public string title_language { get; set; }
public string description { get; set; }
public string description_language { get; set; }
}
Now What I can think of writing LINQ to get MovieDetails object containing reponse but that wont help.
var movieResponse = from MoviesListRootObject movieDetail in rootObj
select new MovieDetails
{
Response =movieDetail.response
};
It's not really clear what you are trying to achieve. My answer assumes that you simply want to have the properties of all Responses in all MoviesListRootObject:
var result = rootObj.SelectMany(x => x.response)
.Select(x => new { x.id, x.title, x.description });
You don't even need an anonymous class here:
var result = rootObj.SelectMany(x => x.response);
// result will be of type IEnumerable<Response>
var movieResponse = rootObj
.SelectMany(m => m.response)
.Select(m => new {
id = m.id, // or just m.id, as name is the same
title = m.title, //idem
description = m.description //idem
});
Are you asking about SelectMany?
var allResponses = rootObj.SelectMany(d => d.Response);
Will give you all the Responses for all movies in rootObj.
if rootObj is in fact an instance of MoviesListRootObject you don't need SelectMany,
var responses = rootObj.response;
will do.

LINQ is returning class name and not value data

I have a collection of TwitterCollection objects held within a List. I am populating the TwitterCollection object (tc) via a foreach loop, and then accessing it through LINQ.
My class with its properties looks like this:
//simple field definition class
public class TwitterCollection
{
public string origURL { get; set; }
public string txtDesc { get; set; }
public string imgURL { get; set; }
public string userName { get; set; }
public string createdAt { get; set; }
public string realURL { get; set; }
public string googleTitle { get; set; }
public string googleDesc { get; set; }
}
I then go on to populate it with a loop through a bunch of RegEx matches in a group:
var list = new List<TwitterCollection>();
foreach (Match match in matches)
{
GroupCollection groups = match.Groups;
var tc = new TwitterCollection
{
origURL = groups[1].Value.ToString(),
txtDesc = res.text,
imgURL = res.profile_image_url,
userName = res.from_user_id,
createdAt = res.created_at,
};
list.Add(tc);
}
Finally I am looking at the collection with LINQ and extracting only certain items for display:
var counts = from URL in list
group URL by URL into g
orderby g.Count()
select new { myLink = g.Key, Count = g.Count() };
The outcome of all this is a long list of the word "TwitterCollection" in count.myLink and no count of URLs...
I used to have this all working before I shifted to a generic List. Now I have moved for convenience, it won't work.
I'd seriously appreciate someone taking me out of my misery here! Thanks in advance.
Your list is of type List<TwitterCollection>, so the URL variable is of type TwitterCollection. So a TwitterCollection is what gets selected into g.Key (and hence myLink), and that gets rendered as the string "TwitterCollection".
Change your query to:
var counts = from tc in list
group tc by tc.origURL into g // note by tc.origURL to extract the origURL property
...
(It's not clear from your code which URL you want to group on, as a TwitterCollection contains several URLs. I've used origURL as an example.)

Categories

Resources