Fetch value from arraylist through linq in C# - c#

I have an array list in this format given below in mongodb collection.
"Students":
[
null,
{
"name": "Rahul",
"RegID": "A01"
},
{
"name": "Raj",
"RegID": "A012"
}
]
I want to display name in string (Rahul,Raj).This is how I am trying.
Var Namelist = students.Select(x =>x?.name)?.ToList();
String names = string.Join(",",Namelist);
But it also appends (,Rahul,Raj).
I can do this by using loop but i want to write the exact linq query

You just need to add a filter to ignore empty entries. You can either do that in the source query:
var Namelist = students.Where(s => s != null).Select(x =>x.name).ToList();
(Note that I've removed a few null operators since you can assume that all of the values are not null)
Or when you join the strings:
String names = string.Join(",", Namelist.Where(s => s != null));
It's generally odd to have a null value in a list of related data, however, so might actually have a problem in the source data that you should fix.

Related

LINQ Query returning null, extracting object attribute value from JSON file

I am trying to extract a country abbreviation for a specific country name from a JSON file. Here is my JSON file:
[
{
"country": "Iceland",
"abbr": "IS"
},
{
"country": "Kosovo",
"abbr": "KS"
},
{
"country": "Belgium",
"abbr": "BE"
},...
]
Here is the model for country:
public class Country
{
public string country { get; set; }
public string abbr { get; set; }
}
I am using the following LINQ query for extracting the given abbreviation and this is returning null for the variable abbreviation:
var abbreviation = from n in countries
where n.country.Equals(selectedCountry)
select n.abbr;
I able to retrieve the list of coutries (first line of LINQ query, I checked using the debugger) and I have selectectedCountry equal to "Iceland". So I am suspecting the problem is coming from the last 2 lines of the LINQ query, I am not really familiar with LINQ queries so I don't know if this is legal. I am operating on one single data table as well so i don't know if that means I should be using a specific type of query.
If I were debugging it I would set breakpoints before and after the query to see what the data it's referring to looks like.
var abbreviation = (from n in countries
where n.country
.Equals(selectedCountry, StringComparison.InvariantCultureIgnoreCase) // ignore case
select n.abbr).FirstOrDefault(); // no duplicates expected
This is not tested but should work.

MongoDB C# Driver - Return last modified rows only

The data:
The collection contains a list of audit records and I want to return the last modified items from the collection.
For example:
So the query needs to return Audit 1235 and 1237 Only.
The following statement works in Mongo Shell and returns the data sub-millisecond, I just need to also figure out how to return the entire Collection item instead of just the Id.
db.Forms.aggregate(
{ $group: { _id: "$Id", lastModifiedId: { $last: "$_id" } } }
)
However, I need to convert this to the C# Driver's syntax.
I have the following at the moment but it's not working and returns (for lack of a better term) weird data (see screencap under the statement).
var results = collection.Aggregate()
.Group(new BsonDocument { { "_id", "$Id" }, { "lastModifiedId", new BsonDocument("$last", "_id") } })
.ToListAsync().Result.ToList();
My current solution gets the full collection back and then runs it through an extension method to get the latest records (where list is the full collection):
var lastModifiedOnlyList =
from listItem in list.OrderByDescending(_ => _.AuditId)
group listItem by listItem.Id into grp
select grp.OrderByDescending(listItem => listItem.AuditId)
.FirstOrDefault();
While this code works, it is EXTREMELY slow because of the sheer amount of data that is being returned from the collection, so I need to do the grouping on the list as part of the collection get/find.
Please let me know if I can provide any additional information.
Update: With Axel's help I managed to get it resolved:
var pipeline = new[] { new BsonDocument { { "$group", new BsonDocument { { "_id", "$Id" }, { "LastAuditId", new BsonDocument { { "$last", "$_id" } } } } } } };
var lastAuditIds = collection.Aggregate<Audit>(pipeline).ToListAsync().Result.ToList().Select(_=>_.LastAuditId);
I moved that to it's own method and then use the IDs to get the collection items back, with my projection working as well:
var forLastAuditIds = ForLastAuditIds(collection);
var limitedList = (
projection != null
? collection.Find(forLastAuditIds & filter, new FindOptions()).Project(projection)
: collection.Find(forLastAuditIds & filter, new FindOptions())
).ToListAsync().Result.ToList();
"filter" in this case is either an Expression or a BsonDocument. The performance is great as well - sub-second for the whole thing. Thanks for the help, Axel!
I think you're doing an extra OrderBy, this should do:
var lastModifiedOnlyList =
from listItem in list
group listItem by listItem.Id into grp
select grp.OrderByDescending(listItem => listItem.AuditId)
.FirstOrDefault();
EDIT:
To gain performance in the query, you could use the Aggregate function differently:
var match = new BsonDocument
{
{
"$group",
new BsonDocument
{
{ "_id", "$Id" },
{ "lastModifiedId", new BsonDocument
{
{
"$last", "$_id"
}
}}
}
}
};
var pipeline = new[] { match };
var result = collection.Aggregate(pipeline);
That should be the equivalent of your Mongo Shell query.

Querying JSON from C#

I'm trying to retreive a json item from a json string,this is my json for example:
{
"users":{
"john":{
"password":"0506777031",
"level":1
},
"doe":{
"password":"john",
"level":1
},
"dasda":{
"password":"das",
"level":"1"
},
"zuri":{
"password":"zuri123",
"level":2
}
}
}
I use the json.net library,this is what i've tried so far:
JObject json = JObject.Parse(jsonstring); //this is thr string
JObject match = json["users"].Values<JObject>().Where(m => m["username"].Value<string>() == "itapi" && (m["password"].Value<string>() == "0506777031")).FirstOrDefault();
I'm getting an error on the second line.
This is the error:
Cannot cast Newtonsoft.Json.Linq.JProperty to Newtonsoft.Json.Linq.JToken.
I'm not sure what i'm doing wrong,i will appreciate any help! thanks!
Assuming your question is "What am I doing wrong?", the answer would be
You are trying to typecast what is a JProperty into a JObject (JProperty has a property named Value you can access).
You are not traversing the JSON syntax tree properly.
There is no mention of the "username" within the JSON sample provided.
If the usernames in your example are the property keys (names) "john", "doe", "dasda" and "zuri"... The query you probably want is as follows:
var match = json["users"].Values<JProperty>().Where(m => m.Name == "doe" && m.Value["password"].ToString() == "john").FirstOrDefault();
EDIT: Alternatively, if the username is that key, you can use the direct lookup and assign to the variable match only if the password matches the one you are trying to compare. Also the following version will return the JObject and not the JProperty as it seems you originally wanted. This should also be more efficient.
JObject match;
var temp = json["users"]["doe"];
if(temp["password"].ToString() == "john")
{
match = temp.ToObject<JObject>();
}
Shouldn't it be using square brackets for "users"?
{
"users":[
"john":{
"password":"0506777031",
"level":1
},
"doe":{
"password":"john",
"level":1
},
"dasda":{
"password":"das",
"level":"1"
},
"zuri":{
"password":"zuri123",
"level":2
}
]
}

List of objects where the object contains an array - how to use LINQ to search through all objects for a value in that array

So I have a list of objects and the object class itself contains an array that holds multiple values. How can I search through all of the objects in the lists' arrays to look for that value?
Example:
[
{
"ObjArray": ["1234", 123"],
"Property1": "60",
"Property2": "64"
},
{
"ObjArray": ["4321", 321"],
"Property1": "112",
"Property2": "22"
},
{
"ObjArray": ["9999"],
"Property1": "2",
"Property2": "2"
}
]
And I want to look for "9999" in all of the "ObjArray"s. How can I do that with LINQ?
EDIT
As Habib pointed out, I just needed a simple Contains clause. Working code looks like this:
var result = mainList.Where(r => r.ObjArray != null && r.ObjArray.Contains("9999", StringComparer.OrdinalIgnoreCase)).FirstOrDefault();
You can do:
var query = mainList.Where(r => r.ObjArray.Contains("9999"));
Or
var query = mainList.Where(r => r.ObjArray.Any(o => o == "9999"));
(Aside from that, your JSON appears invalid, Second value in the array needs a starting double quote)
["1234", 123"]
//^^

How can I determine which value occurs the most in my collection?

So, I have a json file that has a list of fruits. Fruits key can map to a single fruit or a collection of fruits.
E.g:
[
{
"fruits": [
"banana"
]
},
{
"fruits": [
"apple"
]
},
{
"fruits": [
"orange",
"apple"
]
}
]
I was wondering, how can I determine which fruit(s) occur the most in my json structure? That is, how do I know my how often a value occurs and which one is leading above the others?
Not sure if you're interested in having a class to deserialize into, but here's how you would do it. Feel free to skip the class and use dynamic deserialization:
class FruitCollection
{
string[] Fruits { get; set; }
}
var fruitColls = JsonConvert.DeserializeObject<FruitCollection>(json);
var mostCommon = fruitColls
.SelectMany(fc => fc.Fruits)
.GroupBy(f => f)
.OrderByDescending(g => g.Count())
.First()
.Key;
EDIT:
This question's pretty old, but I'll mention that the OrderByDescending, First thing is doing redundant work: you don't really need to sort to get the maximum. This is an age-old lazy hack that people keep doing because LINQ does not provide a nice MaxBy extension method.
Usually your input size is small enough and the other stuff adds enough overhead that you don't really care, but the "correct" way (e.g. if you had billions of fruit types) would be to use a proper MaxBy extension method or hack something out of Aggregate. Finding the max is worst-case linear, whereas sorting is worst case O(n log(n)).
If you use Json.NET, you can load your json using LINQ to JSON, then use SelectTokens to recursively find all "fruits" properties, then recursively collect all descendants string values (those of type JValue), group them by their string value, and put them in descending order:
var token = JToken.Parse(jsonString);
var fruits = token.SelectTokens("..fruits") // Recursively find all "fruit" properties
.SelectMany(f => f.DescendantsAndSelf()) // Recursively find all string literals undernearh each
.OfType<JValue>()
.GroupBy(f => (string)f) // Group by string value
.OrderByDescending(g => g.Count()) // Descending order by count.
.ToList();
Or, if you prefer to put your results into an anonymous type for clarity:
var fruits = token.SelectTokens("..fruits") // Recursively find all "fruit" properties
.SelectMany(f => f.DescendantsAndSelf()) // Recursively find all string literals undernearh each
.OfType<JValue>()
.GroupBy(f => (string)f) // Group by string value
.Select(g => new { Fruit = (string)g.Key, Count = g.Count() } )
.OrderByDescending(f => f.Count) // Descending order by count.
.ToList();
Then afterwards:
Console.WriteLine(JsonConvert.SerializeObject(fruits, Formatting.Indented));
Produces:
[
{
"Fruit": "apple",
"Count": 2
},
{
"Fruit": "banana",
"Count": 1
},
{
"Fruit": "orange",
"Count": 1
}
]
** Update **
Forgot to include the following extension method
public static class JsonExtensions
{
public static IEnumerable<JToken> DescendantsAndSelf(this JToken node)
{
if (node == null)
return Enumerable.Empty<JToken>();
var container = node as JContainer;
if (container != null)
return container.DescendantsAndSelf();
else
return new [] { node };
}
}
The original question was a little vague on the precise structure of the JSON which is why I suggested using Linq rather than deserialization.
The serialization class for this structure is simple:
public class RootObject
{
public List<List<string>> fruits { get; set; }
}
So to deserialize:
var fruitListContainer = JsonConvert.DeserializeObject<RootObject>(jsonString);
Then you can put all fruits in one list:
List<string> fruits = fruitListContainer.fruits.SelectMany(f => f);
Now you have all fruits in one list, and you can do whatever you want. For sorting, see the other answers.
Assuming that the data is in a file named fruits.json, that jq (http://stedolan.github.io/jq/) is on the PATH, and that you're using a Mac or Linux-style shell:
$ jq 'reduce (.[].fruits[]) as $fruit ({}; .[$fruit] += 1)' fruits.json
{
"banana": 1,
"apple": 2,
"orange": 1
}
On Windows, the same thing will work if the quotation marks are suitably adjusted. Alternatively, if the one-line jq program is put in a file, say fruits.jq, the following command could be run in any supported environment:
jq -f fruits.jq fruits.json
If the data is coming from some other process, you can pipe it into jq, e.g. like so:
jq -f fruits.jq
One way to find the maximum count is to add a couple of filters, e.g. as follows:
$ jq 'reduce (.[].fruits[]) as $fruit ({}; .[$fruit] += 1) |
to_entries | max_by(.value)' fruits.json
{
"key": "apple",
"value": 2
}

Categories

Resources