I have a list that will currently return something like this. the Att column can be anything because a user can enter in a Att and Value at anytime.
var attr_vals = (from mpav in _db.MemberProductAttributeValues
select mpav);
Results
Id Att Value
1 Color Blue
1 Size 30
1 Special Slim
3 Color Blue
4 Size 30
2 Special Slim
2 Random Foo Foo
The conversion I am looking for would be similar to this
Converted results
Id Color Size Special Random
1 Blue 30 Slim null
2 null null null Foo Foo
3 Blue null null null
4 null 52 null null
Class looks like this so far.
public class MyMappProducts
{
public int? id { get; set; }
public Dictionary<string, string> Attributes { get; set; }
string GetAttribute(string aName)
{
return Attributes[aName];
}
void setAttribute(string aName, string aValue)
{
Attributes[aName] = aValue;
}
}
So giving that your list of attributes might change, creating a class with each attribute as a property would not be good as you'll have to know all the attributes before hand, thus working with a dictionary is easier.
Here's a way of doing what you want (Note that the missing attributes of each row aren't present in the dictionary):
var list = new List<AttributeValue>
{
new AttributeValue(1, "Color", "Blue"),
new AttributeValue(1, "Size", "30"),
new AttributeValue(1, "Special", "Slim"),
new AttributeValue(3, "Color", "Blue"),
new AttributeValue(4, "Size", "30"),
new AttributeValue(2, "Special", "Slim"),
new AttributeValue(2, "Random", "Foo Foo")
};
// First we groupby the id and then for each group (which is essentialy a row now)
// we'll create a new MyMappProducts containing the id and its attributes
var result = list.GroupBy(av => av.Id)
.Select(g => new MyMappProducts
{
id = g.Key,
Attributes = g.ToDictionary(av => av.Attribute, av => av.Value)
})
.ToList();
This results in (pretty printed):
[
{
"id": 1,
"Attributes": {
"Color": "Blue",
"Size": "30",
"Special": "Slim"
}
},
{
"id": 3,
"Attributes": {
"Color": "Blue"
}
},
{
"id": 4,
"Attributes": {
"Size": "30"
}
},
{
"id": 2,
"Attributes": {
"Special": "Slim",
"Random": "Foo Foo"
}
}
]
Related
I have an object in the db which contains an array of strings.
{
"_id": ObjectId("abc123..."),
"Lines":[
"string 1",
"string 2",
"string 3",
"...",
"string 100"
]
}
I need to select rows by their sequence number in one query.
For example, I need lines 1, 7 and 15.
var projection = Builders<ContentEntity>.Projection
.Slice(e => e.Lines2, 1, 1)
.Slice(e => e.Lines2, 7, 1)
.Slice(e => e.Lines2, 15, 1);
var entity = await GetContext(categoryId).ContentEntities
.Find(e => e.Id == contentId)
.Project<ContentEntity>(projection)
.FirstOrDefaultAsync(cancellationToken);
return entity;
If I use the slice operator like this, then I only get row #15.
I need to select rows by their sequence number in one query from Mongodb with ะก#.
Think that the last .Slice() overwrite the previous .Slice().
Pass the query as BsonDocument instead of using the MongoDB Driver Fluent API which is difficult to implement for your scenario.
Query
db.collection.find({},
{
Lines2: {
$filter: {
input: "$Lines",
cond: {
$in: [
{
$indexOfArray: [
"$Lines",
"$$this"
]
},
[
0,
6,
14
]
]
}
}
}
})
Demo # Mongo Playground
MongoDB .NET Driver syntax
var projection = new BsonDocument
{
{
"Lines", new BsonDocument
{
{
"$filter", new BsonDocument
{
{ "input", "$Lines" },
{
"cond", new BsonDocument
{
{
"$in", new BsonArray
{
new BsonDocument("$indexOfArray", new BsonArray
{
"$Lines",
"$$this"
}),
new BsonArray { 0, 6, 14 }
}
}
}
}
}
}
}
}
};
I am solving an issue where I want to remove unwanted objects from a list List<RegionData> as a result of a region name condition.
As an entry input I receive var data , which contains a List<CasesDto>. One item CasesDto corresponds to all region-based data received on one particular date.
These are two of my classes that I am using as an example:
public class CasesDto
{
public DateTime Date {get; set;}
public List<RegionData> Region {get; set;}
}
and
public class RegionData
{
public string RegionName {get; set;}
public int DailyActiveCases {get; set;}
public int DeceasedToDate {get; set;}
}
This is the current output example (I only included two dates (= two CasesDto) for easy-to-view purpose) where region is not taken into consideration:
[
{
"date": "2021-05-22T00:00:00",
"region": [
{
"regionName": "ce",
"dailyActiveCases": 615,
"deceasedToDate": 568
},
{
"regionName": "kk",
"dailyActiveCases": 170,
"deceasedToDate": 197
},
{
"regionName": "kp",
"dailyActiveCases": 278,
"deceasedToDate": 166
}
]
},
{
"date": "2021-05-23T00:00:00",
"region": [
{
"regionName": "ce",
"dailyActiveCases": 613,
"deceasedToDate": 570
},
{
"regionName": "kk",
"dailyActiveCases": 167,
"deceasedToDate": 197
},
{
"regionName": "kp",
"dailyActiveCases": 277,
"deceasedToDate": 166
}
]
}
]
This is the output I would want if regionName="ce" is applied:
[
{
"date": "2021-05-22T00:00:00",
"region": [
{
"regionName": "ce",
"dailyActiveCases": 615,
"deceasedToDate": 568
}
]
},
{
"date": "2021-05-23T00:00:00",
"region": [
{
"regionName": "ce",
"dailyActiveCases": 613,
"deceasedToDate": 570
}
]
}
]
I would like to solve this problem with lambda functions.
My attempt:
data = data.ForEach(x => x.Region.Where(t => t.RegionName == region)).ToList();
However, I receive a mismatch in return type. How can I access RegionData model and select only a region item (based on region identifier) that I want?
var cases = new List<CasesDto>{
new CasesDto{
Date = DateTime.Now,
Region = new List<RegionData> {
new RegionData{
RegionName = "CE"
},
new RegionData{
RegionName = "DE"
}
}
},
new CasesDto{
Date = DateTime.Now.AddDays(10),
Region = new List<RegionData> {
new RegionData{
RegionName = "DE"
}
}
}
};
var filteredCases = new List<CasesDto>();
var regionName = "CE";
cases.ForEach(c => {
if(c.Region.Any(x => x.RegionName == regionName)){
c.Region = c.Region.Where(x => x.RegionName == regionName).ToList();
filteredCases.Add(c);
}
});
Somewhat simplified to save me some time. Using ForEach we enumerate over the list and check if we have a region with given regionnames. If so we filter the current list to only contain the correct regions and append this result to a new list.
So you have an enumerable sequence of CasesDtos (plural noun), where every CasesDto (singular noun) has at least properties Date and Region.
Region is an enumerable sequence of RegionDatas. Every RegionData has several properties, among which a property RegionName.
I think you've described the following requirement:
Requirement: given a string value for regionName, and a sequence of CasesDtos, give me the Date and the RegionData of every CasesDto that has at least one RegionData with a value for property RegionName that equals regionName.
You didn't tell what you want if a CasesDto has two RegionData with the correct RegionName:
string regionName = "CA";
var casesDto = new CasesDto
{
Date = ...
Region = new List<RegionData>()
{
new RegionData()
{
RegionName = "CA",
...
},
new RegionData()
{
RegionName = "CA",
...
}
}
}
casesDto has two RegionDatas with a matching RegionName. What do you want in your end result: only the Date and one of the RegionDatas? which one? Or do you want all RegionDatas with a matching RegionName?
Anyway, the solution is:
string regionName = "CA";
IEnumerable<CasesDto> casesDtos = ...
// from every CasesDto, make one object, containing the Date, and a property
// Region, which contains only those RegionDatas that have a value for property
// RegionName that equals regionName
var result = casesDtos.Select(casesDto => new
{
Date = casesDto.Date,
// keep only those Regions that have a matching RegionName:
Region = casesDto.Region
.Where(region => region.RegionName == regionName)
.ToList(),
}
// From this selection, keep only those object that have at least one Region:
.Where(casesDto => casesDto.Region.Any());
In words: for every CasesDto in casesDtos, make one new object with two properties: the Date of the CasesDto, and a property Region which contains the RegionDatas of this CasesDto that have a matching RegionName. From this Select result, keep only those object that have at least one element in list Region.
Suppose I have the following object
public string Quote { get; set; }
public string FirstName { get; set; }
I have a List of this object, sample data as:
I am trying to return a new list which will evenly distribute between each FirstName (Same amount) and return 50% of the rows.
In the above Example, I have 20 rows, returning half gives 10. There are 4 different FirstNames to which John = 2, Mark = 2, Phil = 2, bob = 2 - There are 2 slots remaining which 2 Different random names are chosen.
So how do i do the grouping so the FirstNames are evenly distributed? and is the 50% taken first or last?
New to LINQ and filtering through data so help is appreciated :)
LinQ is not the most efficient way As we will be ieterating multiple time the same collection in roder to get basic count etc.
But lets do it Step by step.
Data generation:
That part was missing from the original question. You can change the repatition of name adding duplicate values in names collection.
// Data generation, Tuple because Im lazy.
var names = new[] { "Toto", "John", "John", "John", "Foo", "Foo", "Bar" };
var datas = Enumerable.Range(0, 100)
.Select(x => (Id: x + 1, Name: names[x % names.Length]));
Basic Count:
As we are given a percentage we need to know how many element it represent.
// Input value
var wantedPercent = 10;
// Counting, may be more efficient to iterate that list only once doing grouping and both count in one go
var totalCount = datas.Count();
var distinctNames = datas.Select(x => x.Name).Distinct().Count();
int wantedSize = (int)Math.Ceiling(totalCount * (wantedPercent / 100.0));
int maxGroupSize = wantedSize / distinctNames; // Integer division : 1.9999 -> 1
Grouping:
This is the core issue. We need to group by on name.
And for each element of those group we will number them based on maxsize of a group.
If that number exceed the max size we keep them for remaining slots.
var grpDatas = datas.GroupBy(x => x.Name)
.SelectMany(g =>
g.Select((x, i) =>
new { Item =x, newGroupingKey = i < maxGroupSize}
)
)
.GroupBy(x=> x.newGroupingKey);
Populate :
Let's take the item of the first group.
var result = grpDatas.First(x=> x.Key)
.Select(g=> g.Item)
.ToList();
You can have a remaining slot even if wantedSize / distinctNames is round.
Because one group size may be lower than the Max Allowed Size.
var remainingSlot = wantedSize - result.Count();
if (remainingSlot > 0){
var random = new Random();
var shuffledGroup2 = grpDatas.First(x => !x.Key)
.Select(g => g.Item)
.OrderBy(a => random.Next());
var remaining = shuffledGroup2.Take(remainingSlot);
result.AddRange(remaining);
}
Result : Live Demo
[
{ "Id": 1, "Name": "Toto" },
{ "Id": 8, "Name": "Toto" },
{ "Id": 2, "Name": "John" },
{ "Id": 3, "Name": "John" },
{ "Id": 5, "Name": "Foo" },
{ "Id": 6, "Name": "Foo" },
{ "Id": 7, "Name": "Bar" },
{ "Id": 14, "Name": "Bar" },
{ "Id": 83, "Name": "Foo" }, // random
{ "Id": 85, "Name": "Toto" } // random
]
Bear with me, I will try to not make this too complicated. I have a list of products. A product looks like this:
{
"id": 3797,
"title": "Canon EOS 100D Digital SLR Camera with 18-55 IS STM Lens, HD 1080p, 18MP, 3\" LCD Touch Screen",
"shortTitle": "Canon EOS 100D Black",
"brand": "Canon",
"model": "EOS 100D",
"colour": "Black",
"gtin": "8714574602721",
"image": "http://piiick.blob.core.windows.net/images/Canon-EOS-100D-18-55-Black-8714574602721.png",
"type": "Digital SLR",
"lensFocalLength": "18-55",
"lensType": "IS STM",
"lensMount": "EF/EF-S",
"maxAperture": "999",
"connectivity": "",
"shootingModes": "Scene Intelligent Auto (Stills and Movie), No Flash, Creative Auto, Portrait, Landscape, Close-up, Sports, SCN(Kids, Food, Candlelight, Night Portrait, Handheld Night Scene, HDR Backlight Control), Program AE , Shutter priority AE, Aperture priority AE, Manual (Stills and Movie)",
"weight": 410.0,
"width": 116.8,
"height": 90.7,
"depth": 69.4,
"digitalZoom": "N/A",
"opticalZoom": "N/A",
"waterproof": false,
"maxVideoResolution": "1920 x 1080",
"sensorType": "CMOS",
"sensorSize": "22.3 x 14.9 mm",
"continuousShootingSpeed": "4",
"iso": "1600",
"style": "traditional",
"designer": "",
"dateAnnounced": "10/06/2008",
"focusPoints": 7
}
But to help explain this I will simplify it.
I am trying to create a sort method that will allow sorting on multiple columns.
For example, I have this method:
/// <summary>
/// Performs an advanced sort on products using sortations
/// </summary>
/// <param name="products">The products to sort</param>
/// <param name="sortations">The sortation list</param>
public void AdvancedSort(List<JObject> products, List<Sortation> sortations)
{
// Get our value types
GetTypesFromOperator(sortations);
// Sort our products by the sortations
products.Sort((a, b) =>
{
foreach (var sortation in sortations)
{
// Get our values
var fieldName = sortation.Field;
var x = a.SelectToken(fieldName).ToString();
var y = b.SelectToken(fieldName).ToString();
var type = sortation.Type;
// If both values are the same, skip the rest of this iteration
if (x.Equals(y)) return 0;
// If we have an expression
if (!string.IsNullOrEmpty(sortation.Expression))
{
// If we are checking for between
if (sortation.Operator == "><")
{
// Get our values
var values = sortation.Expression.Split(',');
// If we have 2 values
if (values.Length == 2)
return CompareBetween(values, x, y);
}
// If we are checking booleans
if (sortation.Operator == "===")
return CompareBoolean(sortation.Expression);
// If we are checking equals
if (sortation.Operator == "=")
return y.Equals(sortation.Expression) ? 1 : -1;
// If we are checking like
if (sortation.Operator == "%")
return y.Equals(sortation.Expression, StringComparison.OrdinalIgnoreCase) ? 1 : -1;
}
// If we get this far, do a switch on sortation types
switch (sortation.Type)
{
case SortationValueType.Boolean:
return Boolean.Parse(x) ? -1 : 1;
case SortationValueType.Number:
return CompareNumeric(x, y, sortation.Direction);
default:
return string.CompareOrdinal(x, y);
}
}
// If nothing matches, return 0
return 0;
});
}
It is trying to sort based on property value types and what I call sortations.
A sortation is just a class that stipulates the operator, expression and field to query.
This is an example of a sortation:
// Add importance, expert and sponsored
sortations.Add(new Sortation
{
Field = "importance",
Operator = "<"
});
Now, we can have an number of different sortations which are dynamically added. I need to be able to sort the products by these sortations.
I have created a simple test:
[Test]
public void SortBySilverColourSilverShouldBeTop()
{
// Assemble
var json = "[{ colour: 'Black', importance: '150' }, { colour: 'Black', importance: '150' }, { colour: 'Black', importance: '150' }, { colour: 'Silver', importance: '150' }, { colour: 'Black', importance: '149' }, { colour: 'Black', importance: '149' }, { colour: 'Black', importance: '149' }, { colour: 'Silver', importance: '149' }]";
var products = JsonConvert.DeserializeObject<List<JObject>>(json);
var sortations = new List<Sortation>() { new Sortation() { Field = "colour", Operator = "=", Expression = "Silver" }, new Sortation() { Field = "importance", Operator = "<" } };
// Act
_sortProvider.AdvancedSort(products, sortations);
var result = products[0].SelectToken("colour").ToString();
// Assert
Assert.That(result, Is.EqualTo("Silver"));
}
I would expect with that, that I would get this output:
[
{ colour:'Silver', Importance:'150'},
{ colour:'Black', Importance:'150' },
{ colour:'Black', Importance:'150' },
{ colour:'Black', Importance:'150' },
{ colour:'Silver', Importance:'149' },
{ colour:'Black', Importance:'149' },
{ colour:'Black', Importance:'149' },
{ colour:'Black', Importance:'149' }
]
but instead I get this output:
[
{ colour:'Silver', Importance:'150' },
{ colour:'Silver', Importance:'149' },
{ colour:'Black', Importance:'150' },
{ colour:'Black', Importance:'150' },
{ colour:'Black', Importance:'150' },
{ colour:'Black', Importance:'149' },
{ colour:'Black', Importance:'149' },
{ colour:'Black', Importance:'149' }
]
Can someone help me fix my sort method?
In my mind your basic problem is that as soon as the first sortation hits a match it returns the sort order value to the sort function and the rest of the sortations don't matter at all. I believe what you need to do is swap the foreach and the products.sort call, like so:
// Get our value types
GetTypesFromOperator(sortations);
foreach (var sortation in sortations)
{
// Sort our products by the sortations
products.Sort((a, b) =>
{
// Get our values
var fieldName = sortation.Field;
var x = a.SelectToken(fieldName).ToString();
var y = b.SelectToken(fieldName).ToString();
var type = sortation.Type;
....
....
}
}
This will take longer, but each sortation will be considered. Keep in mind that the last sortation in the list will be the most significant.
It looks like you've got them backwards - it puts all the "Silver"s first, then ordering by importance second.
Try swapping the order of your sortations:
var sortations = new List<Sortation>() {
new Sortation() { Field = "importance", Operator = "<" },
new Sortation() { Field = "colour", Operator = "=", Expression = "Silver" }
};
I have created a JSON file from JArray in MVC application from 2 coloumns of my Database. But there are 2 issues:
1. The format of the file. It has extra brackets for each object.
[
[
{
"Code": "1",
"Name": "ASSETS"
}
],
[
{
"Code": "2",
"Name": "LIABILITIES"
}
],
[
{
"Code": "3",
"Name": "CAPITAL"
}
],
[
{
"Code": "4",
"Name": "REVENUE"
}
]
]
I want it as:
[
{
"Code": "1",
"Name": "ASSETS"
},
{
"Code": "2",
"Name": "LIABILITIES"
},
{
"Code": "3",
"Name": "CAPITAL"
},
{
"Code": "4",
"Name": "REVENUE"
}
]
I have loaded the values in JArray from Database and then add it in another JArray. I am using 2 for loops. 1 for making JArray for 5000 entries. And the second for returning the next valid primary key from Database. But the problem is that, it takes more than 15 minutes to process the loops and return the JSON file. Why is there so much latency? And how can I make it fast. Here is the code.
int idd =0;
JArray Array = new JArray();
for (int b = 0; b<5000; b++)
{
idd = dbid(idd);
IEnumerable<MST> accList = new List<MST>
{
new MST
{
S1 = db.MSTs.Find(idd).S1,
S2 = db.MSTs.Find(idd).S2
}
};
JArray Arrayone = new JArray(
accList.Select(p => new JObject
{
{ "Code", p.S1 },
{ "Name", p.S2 },
})
);
Array.Add(Arrayone);
}
string jsonfile = JsonConvert.SerializeObject(Array,Formatting.Indented);
string path = #"C:\Users\Awais\Desktop\accounts.json";
System.IO.File.WriteAllText(path, jsonfile);
return View(v);
}
public int dbid(int id)
{
decimal i = db.MSTs.Max(a => a.N100);
MST m = new MST();
for (; id <= i; id++)
{
m = db.MSTs.Find(++id);
if (m == null)
continue;
else
{
break;
}
}
return id;
}
When I tried the first loop for 100 entries, it took about 60 seconds to return the file.
By modeling your JSON with anonymous type, you could've done it like this:
var array = (from coa in db.MSTs
select new { Code = coa.S2, Name = coa.S1 }).ToArray();
string jsonfile = JsonConvert.SerializeObject(array, Formatting.Indented);
string path = #"C:\Users\Awais\Desktop\accounts.json";
System.IO.File.WriteAllText(path, jsonfile);
It took me 319 ms for 100 000 objects.
Try:
public class RootObject
{
public string Code { get; set; }
public string Name { get; set; }
}
var o = new List<RootObject>();
for (var i = 0; i < 100; ++i)
{
o.Add(new RootObject
{
Code = "foo",
Name = "bar"
});
}
var v = JsonConvert.SerializeObject(o);
Took around 274 milliseconds for my list to Serialize.
I have removed all loops and instead try to solve it in query and making a single array.
var k = (from coa in db.MSTs
select new { S2 = coa.S2, S1 = coa.S1 }).ToList().
Select(x => new MST { S2 = x.S2, S1 = x.S1 }).ToList();
JArray Arrayone = new JArray(
k.Select(p => new JObject
{
{ "Code", p.S1 },
{ "Name", p.S2 },
})
);
string jsonfile = JsonConvert.SerializeObject(Arrayone,Formatting.Indented);
string path = #"C:\Users\Awais\Desktop\accounts.json";
System.IO.File.WriteAllText(path, jsonfile);
It solves both problems. i.e. Brackets formatting issue and latency. Now with this code it works in less than 5 seconds