Linq query producing incorrect result - c#

Consider the following linq query
var result = from a in
from b in filledTable
join c in distinctList on b[0].SerialNumber equals c.Field("SERIAL NUMBER")
select new { b, c }
group a by new { a.b[0].SerialNumber } into d
select new
{
Id = d.Select(x => x.b[0].Id),
SerialNumber = d.Select(x => x.b[0].SerialNumber),
// This part is not producing the correct output.
ImportTable = d.Select(w => w.c.Table
.AsEnumerable()
.GroupBy(y => y.Field("SERIAL NUMBER"))
.Select(z => z.First())
.CopyToData‌​Table())
};
filledTable in my linq query is a List<dynamic> which is populated by what the values are returned from a sproc and distinctList is a List<DataRow> which I distinct the values coming from the DataTable as follows:
List<DataRow> distinctList = dt.AsEnumerable().Distinct(DataRowComparer.Default).ToList();
My Linq query produces the following JSON
[
{
"FilledTableList":[
[
{
"Id":[
2
],
"SerialNumber":[
"1073410"
],
"ImportTable":[
[
{
"SERIAL NUMBER":"1073410",
"PRODUCT TYPE":"Product A"
},
{
"SERIAL NUMBER":"1073411",
"PRODUCT TYPE":"Product B"
}
]
]
},
{
"Id":[
-1
],
"SerialNumber":[
"1073411"
],
"ImportTable":[
[
{
"SERIAL NUMBER":"1073410",
"PRODUCT TYPE":"Proeduct A"
},
{
"SERIAL NUMBER":"1073411",
"PRODUCT TYPE":"Product B"
}
]
]
}
]
]
}]
But I would like the following JSON output
[
{
"FilledTableList":[
[
{
"Id":[
2
],
"SerialNumber":[
"1073410"
],
"ImportTable":[
[
{
"SERIAL NUMBER":"1073410",
"PRODUCT TYPE":"Product A"
}
]
]
},
{
"Id":[
-1
],
"SerialNumber":[
"1073411"
],
"ImporTable":[
[
{
"SERIAL NUMBER":"1073411",
"PRODUCT TYPE":"Product B"
}
]
]
}
]
]
}]
So the ImportTable node only contains the information matching to the serial number in the above FilleTabledList node. Everything else seems to work as expected by the Linq query apart from this. Can someone tell me where I'm going wrong please
Update:
My filledTable contains two items as follows:
{ Id = 2, SerialNumber = "1073410"}
{ Id = -1, SerialNumber = "1073411"}
Eventually I will have more items in the list but just to figure out why more linq query isn't working I have narrowed it down to just to items

I created a fiddle, which makes it easier to communicate the available data and the expected results.
When I understood it correctly you like to get a list of all products, listed in the filledTable and then find all elements with the same serial number from the dataTable.
If this is correct, than the LINQ query has to be:
var result = filledTable.GroupJoin(distinctList, product => product.SerialNumber, row => row.Field<string>("SERIAL NUMBER"), (Product, Rows) => new { Product, Rows })
.Select(group => new
{
Id = group.Product.Id,
SerialNumber = group.Product.SerialNumber,
ImportTable = group.Rows.CopyToDataTable()
});
and the result will be
[
{
"Id": 2,
"SerialNumber": "1073410",
"ImportTable": [
{
"SERIAL NUMBER": "1073410",
"PRODUCT TYPE": "Product A"
}
]
},
{
"Id": -1,
"SerialNumber": "1073411",
"ImportTable": [
{
"SERIAL NUMBER": "1073411",
"PRODUCT TYPE": "Product B"
}
]
}
]

I am not really sure, but would something like this work?
var result = (from a in (from b in filledTable join c in distinctList on b[0].SerialNumber equals c.Field<string>("SERIAL NUMBER") select new { b, c })
group a by new { a.b[0].SerialNumber } into d
select new
{
Id = d.Select(x => x.b[0].Id),
SerialNumber = d.Select(x => x.b[0].SerialNumber),
ImportTable = d.Select(w => w.c.Table.AsEnumerable()
.Where(y=>y.Field<string>("SERIAL NUMBER") == d.Key.ToString())
.GroupBy(y => y.Field<string>("SERIAL NUMBER")).Select(z => z.First()).CopyToData‌​Table())
});

Here is a simplified query that can be used:
var result =
from entry in filledTable
join row in distinctList on entry[0].SerialNumber equals row.Field<string>("SERIAL NUMBER")
group new { entry, row } by entry[0].SerialNumber into items
select new
{
Id = items.Select(x => x.entry[0].Id),
SerialNumber = new[] { items.Key }.AsEnumerable(),
ImportTable = items.Select(x => x.row).CopyToDataTable()
};
It should be equivalent to the desired output and deal with most strange data combinations that are handled by the original query.

Related

Flatten Json & Ignore array index Using ChoETL

I have 1 json file and these lines of code:
Here's my code:
using (var r = new ChoJSONReader("data.json")
.Configure(c => c.ThrowAndStopOnMissingField = true)
.Configure(c => c.DefaultArrayHandling = true)
.Configure(c => c.FlattenNode = true)
.Configure(c => c.IgnoreArrayIndex = false)
.Configure(c => c.NestedKeySeparator = '.')
.Configure(c => c.NestedColumnSeparator = '.')
)
{
var dt = r.AsDataTable();
Console.WriteLine(dt.DumpAsJson());
}
My data.json file:
{
"BrandId": "998877665544332211",
"Categories": [
"112233445566778899"
],
"Contact": {
"Phone": [
{
"Value": "12346789",
"Description": {
"vi": "Phone"
},
"Type": 1
},
{
"Value": "987654321",
"Description": {
"vi": "Phone"
},
"Type": 1
}
]
}
}
After running this code, I got the output like this:
[
{
"BrandId": "998877665544332211",
"Contact.Phone.0.Value": "12346789",
"Contact.Phone.0.Description.vi": "Phone",
"Contact.Phone.0.Type": 1,
"Contact.Phone.1.Value": "987654321",
"Contact.Phone.1.Description.vi": "Phone",
"Contact.Phone.1.Type": 1,
"Category0": "112233445566778899"
}
]
The question here is how can I get some kind of output json without "0" at the flattened key node
Expected output:
[
{
"BrandId": "998877665544332211",
"Contact.Phone.Value": "12346789",
"Contact.Phone.Description.vi": "Phone",
"Contact.Phone.Type": 1,
"Category": "112233445566778899"
},
{
"BrandId": "998877665544332211",
"Contact.Phone.Value": "987654321",
"Contact.Phone.Description.vi": "Phone",
"Contact.Phone.Type": 1,
"Category": "112233445566778899"
}
]
I've research by many ways but it's results doesn't as same as my expected result.
Thanks for any kind of help
As your json is nested/complex in nature, you need to unpack and flatten into multiple simple data element rows using ChoETL/Linq as below.
ChoETLSettings.KeySeparator = '-';
using (var r = ChoJSONReader.LoadText(json)
.WithField("BrandId")
.WithField("Category", jsonPath: "Categories[0]", isArray: false)
.WithField("Phone", jsonPath: "Contact.Phone[*]")
)
{
var dt = r.SelectMany(rec => ((IList)rec.Phone).OfType<dynamic>().Select(rec1 =>
{
dynamic ret = new ChoDynamicObject();
ret["BrandId"] = rec.BrandId;
ret["Contact.Phone.Value"] = rec1.Value;
ret["Contact.Phone.Description.vi"] = rec1.Description.vi;
ret["Contact.Phone.Type"] = rec1.Type;
ret["Category"] = rec.Category;
return ret;
})).AsDataTable();
dt.DumpAsJson().Print();
}
Sample fiddle: https://dotnetfiddle.net/PHK8LO

C# Linq Group by with Dictionary

I have following data
{
"id": "0012",
"Name": "User01",
"Status": "NEW",
"Urgency": "Urgent"
}, {
"id : "0013",
"Name": "User01",
"Status": "NEW",
"Urgency": "Urgent"
}, {
"id : "0014"
"Name": "User01",
"Status": "REJECTED",
"Urgency": "Urgent"
} {
"id : "0015"
"Name": "User02",
"Status": "NEW",
"Urgency": "PastDue"
}
I am trying to get the below output using Linq GroupBy. But, not able to get the exactly as per the output.
var groupedUrgency = sampleData.GroupBy(x => new { x.Urgency });
var data = groupedUrgency.Select(x => new
{
Name = "NeedToGetTheName also",
NewItems = x.Where(z => z.Status == "NEW").ToDictionary(gdc => x.Key.Urgency, gdc => x.Count()),
RejectedItems = x.Where(z => z.Status == "REJECTED").ToDictionary(gdc => x.Key.Urgency, gdc => x.Count())
})
.ToList();
Is there any way we can the output below mentioned table. I need to get the User name and It's count for given Urgency property for each status.
You can first group by Name and then select count group by Urgency as follows:
var data = sampleData.GroupBy(x => x.Name).Select(x => new
{
Name = x.Key,
NewItems = x.Where(n => n.Status == "NEW").GroupBy(g => g.Urgency).Select(s => new { UrgentType = s.Key, Count = s.Count() }),
RejectedItems = x.Where(n => n.Status == "REJECTED").GroupBy(g => g.Urgency).Select(s => new { UrgentType = s.Key, Count = s.Count() }),
}).ToList();

Formatting api JSON response to add attributes to an array

I have the following api controller that returns a json representation of the query you see here:
public async Task<ActionResult<IEnumerable<CarDto>>> GetCarData(Guid carID)
{
var carData = await (from cl in _context.CarList
join tl in _context.transmissionList
on cl.CId equals tl.CId
join to in _context.transmissionOptions
on tl.TId equals to.TId
where cl.CId == carID
select new CarDto
{
CarId = cl.CarId,
TransmissionId = tl.TId,
OptionId = to.OptionId,
GearId = to.GearId
})
.ToListAsync();
return carData;
}
The returned json data looks like this:
[
{
"carId": "351a",
"transmissionId": "ec7",
"optionId": "a1",
"gearId": "674532a"
},
{
"carId": "351a",
"transmissionId": "ec7",
"optionId": "b7",
"gearId": "5f9173f"
},
{
"carId": "351a",
"transmissionId": "ec7",
"optionId": "c5",
"gearId": "cf807"
}
]
However, I'd like for it to be formatted such that there is a property called transmissionChoices that contains an array of the possible options.
Like this:
{
"carId": "351a",
"transmissionId": "ec7",
"transmissionChoices": [
{
"optionId": "a1",
"gearId": "674532a"
},
{
"optionId": "b7",
"gearId": "5f9173f"
},
{
"optionId": "c5",
"gearId": "cf807"
}
]
}
Is there a way to get the controller to format it like that?
You can use the LINQ GroupBy method and then project the grouped results into the shape you want.
public async Task<ActionResult<IEnumerable<object>>> GetCarData(Guid carID)
{
var carData = await (from cl in _context.CarList
join tl in _context.transmissionList
on cl.CId equals tl.CId
join to in _context.transmissionOptions
on tl.TId equals to.TId
where cl.CId == carID
select new
{
CarId = cl.CarId,
TransmissionId = tl.TId,
OptionId = to.OptionId,
GearId = to.GearId
})
.GroupBy(x => x.CarId)
.Select(g => new
{
CarId = g.First().CarId,
TransmissionId = g.First().TransmissionId,
TransmissionChoices = g.Select(x => new
{
OptionId = x.OptionId,
GearId = x.GearId
})
})
.ToListAsync();
return carData;
}
Note that this is projecting the results into an anonymous type. Feel free to create a model that matches the schema you need and then use that model in the Select(...) projection.

JSON - LINQ Query for object or an array to group two data with same key value pair

Consider I have a JSON data as:
{
"entities":[
{
"republish": false,
"OrgID": "",
"createdby": "730",
"questions": [
{
"sequence": "5",
"QuestionId": "57BB6DDC-A90A-10EE-E224-EC658A825871",
"metadata": [
{
"key": "Group",
"value": 0
},
{
"key": "Part",
"value": "0"
}
]
},
{
"sequence": "4",
"QuestionId": "57BB6DDC-A90A-10EE-E224-EC658A825871",
"metadata": [
{
"key": "Group",
"value": 1
},
{
"key": "Part",
"value": "A"
}
]
},
{
"sequence": "3",
"QuestionId": "57BB6DDC-A90A-10EE-E224-EC658A825871",
"metadata": [
{
"key": "Group",
"value": 1
},
{
"key": "Part",
"value": "B"
}
]
}
]
}
]
}
As you can see I have a list of questions available and in each question, I have a metadata which holds Key-Value pair.
Above example demonstrates, I have 3 questions and out of it 2 question, metadata key-value is "Group 1".
Now I want to do is combine the questions with a same key-value pair and treat it as one.
so in my final case, I will have 2 questions instead of 3. And out of that one question will have two separate questions inside.
And I want to achieve this using Linq query. And if possible please use Newtonsoft for parse if needed. I have been stuck for long onto this.
Things I have done:
public virtual HttpResponseMessage AddQuestionsToStandardMaster(TaxonomyMetaData objQuestion)
{
List<ResponseEntity> objResponseList = new List<ResponseEntity>();
try
{
if (ModelState.IsValid)
{
foreach (var objEntity in objQuestion.Entities)
{
EntityResponse objentityresponse = new EntityResponse();
ResponseEntity objResponse = new ResponseEntity();
}
List<Question> objQuestionList = new List<Question>();
if (objEntity.Questions.Length > 0)
{
foreach (var item in objEntity.Questions)
{
int questionTypeid = 0;
dynamic objQuestionJson = JObject.Parse(item.ToString())
}
}
}
Question objCurrentQuestion = new Question();
Question objQuestionforDelete = new Question();
JObject itemToParese = new JObject();
string SingleQuestionJson = objQuestionJson.GetValue("QuestionData").ToString();
string questionstem = "";
Regex rgx = new Regex("/\'");
objCurrentQuestion.Sequence = Convert.ToInt32(objQuestionJson.GetValue("sequence"));
objCurrentQuestion.tag = objQuestionJson.tag.ToObject<JToken[]>(); ;
objCurrentQuestion.metadata = objQuestionJson.metadata.ToObject<JToken[]>();
objCurrentQuestion.SingleQuestionJson = rgx.Replace(SingleQuestionJson, "'");
objCurrentQuestion.QuestionsType = questionTypeid;
objCurrentQuestion.QuestionsId = new Guid(objQuestionJson.GetValue("QuestionId").ToString());
objCurrentQuestion.VersionNo = Convert.ToInt32(objQuestionJson.GetValue("VersionNo"));
objCurrentQuestion.DisplayQuestionId = Convert.ToString(objQuestionJson.GetValue("DisplayQuestionId"));
objCurrentQuestion.OriginalQuestionId = Convert.ToString(objQuestionJson.GetValue("OriginalQuestionId"));
objCurrentQuestion.PassageText = Convert.ToString(objQuestionJson.GetValue("passage_text"));
objCurrentQuestion.PassageCode = Convert.ToString(objQuestionJson.GetValue("passage_id"));
objCurrentQuestion.PassageTitle = Convert.ToString(objQuestionJson.GetValue("passage_title"));
objCurrentQuestion.IsPublished = Convert.ToByte(true);
objCurrentQuestion.ProductId = objEntity.ProductID;
foreach (var metadata in objCurrentQuestion.metadata)
{
switch (metadata["key"].ToString())
{
case "Group":
objCurrentQuestion.Group = Convert.ToInt32(metadata["value"].ToString());
break;
case "Part":
objCurrentQuestion.Part = metadata["value"].ToString();
break;
}
}
objQuestionList.Add(objCurrentQuestion);
int counter = 1;
//Here I get the data in a group which needs to coverted to JSOn and then replace the original JSON data with this. But I know this is irrelevant to what we need to achieve.
var yui = objQuestionList.Where(tma => tma.Group == counter).Select(t => t).GroupBy(s => new { s.Group }).Where(p => p.Count() > 1).ToList();
//After proper conversion I need to enter this data to a database.
I am a bit unclear on what do you mean by make it
combine the questions with a same key-value pair and treat it as one
But here is how you can group by the JSON into groups in metadata you can do a custom select to what ever you want.
var text = #"{
""entities"":[
{
""republish"": false,
""OrgID"": """",
""createdby"": ""730"",
""questions"": [
{
""sequence"": ""5"",
""QuestionId"": ""57BB6DDC-A90A-10EE-E224-EC658A825871"",
""metadata"": [
{
""key"": ""Group"",
""value"": 0
},
{
""key"": ""Part"",
""value"": ""0""
}
]
},
{
""sequence"": ""4"",
""QuestionId"": ""57BB6DDC-A90A-10EE-E224-EC658A825871"",
""metadata"": [
{
""key"": ""Group"",
""value"": 1
},
{
""key"": ""Part"",
""value"": ""A""
}
]
},
{
""sequence"": ""3"",
""QuestionId"": ""57BB6DDC-A90A-10EE-E224-EC658A825871"",
""metadata"": [
{
""key"": ""Group"",
""value"": 1
},
{
""key"": ""Part"",
""value"": ""B""
}
]
}
]
}
]
}";
var json = JObject.Parse(text);
var groupedData = from entity in json["entities"]
from question in entity["questions"]
group question by question["metadata"][0]["value"] into questionGroup
select questionGroup;
foreach (var data in groupedData)
{
Console.WriteLine("_____________________");
Console.WriteLine("Group");
Console.WriteLine(data.Key);
foreach (var question in data)
{
Console.WriteLine(question["QuestionId"]);
}
Console.WriteLine("_____________________");
}
If "Group" is not always the fist item in the array you can do
question["metadata"].First(md => md.Value<string>("key") == "Group")["value"]
to get it.

Linq in JSON.NET

I have a json string as below
{
'Sheet1': [{
'EmployeeNo': '123456',
'EmployeeName': 'Midhun Mathew'
}, {
'EmployeeNo': '123457',
'EmployeeName': 'Bill Gates'
}, {
'EmployeeNo': '123456',
'Address': 'AAAA'
}, {
'EmployeeNo': '123457',
'Address': 'BBBB'
}]
}
JObject parsedJson = JObject.Parse(jsonString);
// Get the EmployeeNo's
List<JToken> tokenList = parsedJson["Sheet1"]
.GroupBy(d => d["EmployeeNo"])
.Select(s => s.Key)
.ToList();
// Get the tokens which have the Same 'EmployeeNo'
foreach (JToken j in tokenList)
{
IEnumerable<JToken> t = parsedJson["Sheet1"].Where(s => s["EmployeeNo"] == j);
}
But in the foreach I am getting only the first one which is
{
"EmployeeNo": "123456",
"EmployeeName": "Midhun Mathew"
}
I am not getting what I am doing wrong here.
My original intent is to group the JTokens having same EmployeeNo into one JObject
So in the above case I will get 2 JObjects since there are 2 different EmployeeNo's
Hope I am clear
It's not entirely clear what you're trying to do here, but you seem to be grouping by EmployeeNo, then throwing away the results to get just the keys, and then trying to do the grouping again in a more manual way.
If you remove the Select(s => s.Key) part, then you could just use the groupings directly:
var tokensByEmployeeNo = parsedJson["Sheet1"].GroupBy(x => x["EmployeeNo"]);
foreach (var grouping in tokensByEmployeeNo)
{
Console.WriteLine($"Key: {grouping.Key}");
foreach (var token in grouping)
{
Console.WriteLine(token);
}
}
If you want to create a 'new' JObject for each of these groupings, then you probably want to create a JArray per group:
var groupedArrays = parsedJson["Sheet1"]
.GroupBy(x => x["EmployeeNo"])
.Select(groupedRecords => new JArray(groupedRecords));
The first of these would look like this, for example:
[
{
"EmployeeNo": "123456",
"EmployeeName": "Midhun Mathew"
},
{
"EmployeeNo": "123456",
"Address": "AAAA"
}
]
If you just wanted to go through the list of Employees, here's how I am used doing:
var json = #"
{
'Sheet1': [{
'EmployeeNo': '123456',
'EmployeeName': 'Midhun Mathew'
}, {
'EmployeeNo': '123457',
'EmployeeName': 'Bill Gates'
}, {
'EmployeeNo': '123456',
'Address': 'AAAA'
}, {
'EmployeeNo': '123457',
'Address': 'BBBB'
}]
}";
var array = JArray.Parse(JObject.Parse(json).SelectToken("Sheet1").ToString());
foreach (var arrayElement in array)
{
var employeeNo = JObject.Parse(arrayElement.ToString()).SelectToken("EmployeeNo");
var employeeName = JObject.Parse(arrayElement.ToString()).SelectToken("EmployeeName");
Console.WriteLine("Number: {0}, Name: {1}", employeeNo, employeeName);
}
Output:

Categories

Resources