Getting the name of array - c#

I have the following Json:
{
"last_ts": "20161001154251",
"first_ts": "20061130072151",
"years": {
"2006": [
0,0,0,2,0,0,0,0,0,0,1,0
],
"2007": [
0,0,3,0,0,1,0,0,0,0,1,0
],
"2008": [.........],
.
.
.
}
}
I wanted to read each year's name and its corresponding array of numbers, I tried the following code:
JObject jObj = JObject.Parse(json);
var yearsA = jObj["years"];
foreach (var year in yearsA)
{
string yearStr = year. // code here to retrieve this year's name
foreach (var month in year.Children<JArray>().Children()) // loop thru each array value
{
int m = (int)month;
if(m > 0)
{
years.Add(yearStr);
break;
}
}
}
What I want now is just a way to get the name of the array, I tried lot of solutions but none worked for me.

Try this code:
var yearsA = jObj["years"].Cast<JProperty>();
List<string> years = new List<string>();
foreach (var year in yearsA)
{
foreach (var month in year.Children<JArray>().Children()) // loop thru each array value
{
int m = (int) month;
if (m > 0)
{
years.Add(year.Name);
break;
}
}
}

Just declare a class like
public class MyObj
{
public string last_ts { set; get; }
public string first_ts { set; get; }
public Dictionary<int,int[]> years { set; get; }
}
and deserialize as
var data = JsonConvert.DeserializeObject<MyObj>(jsonString);
Sample usage:
foreach(var entry in data.years)
{
int year = entry.Key;
int[] months = entry.Value.Where(m => m > 0).ToArray();
Console.WriteLine(year + " => " + string.Join(",", months));
}

If you want to use Linq to JSON, you can do something like this, but there's lot of other options, as mentioned in another answers.
string json = #"{
'last_ts': '20161001154251',
'first_ts': '20061130072151',
'years': {
'2006': [
0,0,0,2,0,0,0,0,0,0,1,0
],
'2007': [
0,0,3,0,0,1,0,0,0,0,1,0
],
}
}";
JObject jObj = JObject.Parse(json);
// since in your case your years and months are structure as key/value, it's possible to use built in class like Dictionary<TKey, TValue>
var years = jObj["years"].ToObject<Dictionary<string, List<int>>>();
var result = new Dictionary<string, List<int>>();
foreach (var year in years)
{
string key = year.Key;
var value = year.Value;
var months = new List<int>();
value.ForEach(t =>
{
if (t > 0)
{
months.Add(t);
}
});
result.Add(key, months);
}
Look at Convert JObject into Dictionary. Is it possible?
and How do I get the list of keys in a dictionary?

Related

Convert flat JSON to nested JSON

I have a flat JSON like below (I don't know what to call it, I hope that flat is the right word)
{
"id":12947,
"name.first_name":"Honey",
"address.street.number":"23",
"address.city.code":"LL",
"address.street.name":"Narrow Street",
"address.city.name":"Lalaland",
"name.last_name":"Moon",
"books": [
{
"title":"The long story",
"author.first_name":"Brew",
"author.last_name":"Beating",
"type":"novel"
},
{
"title":"Money and morality",
"author.first_name":"Chris",
"author.last_name":"Mas",
"type":"self-help"
}
]
}
Please notice that the fields are not in sorted order.
I want to convert it into a nested JSON like below:
{
"id":12947,
"name":{
"first_name":"Honey",
"last_name":"Moon"
},
"address":{
"street":{
"number":"23",
"name":"Narrow Street"
},
"city":{
"code":"LL",
"name":"Lalaland"
}
},
"books": [
{
"title":"The long story",
"author": {
"first_name":"Brew",
"last_name":"Beating"
},
"type":"novel"
},
{
"title":"Money and morality",
"author":{
"first_name":"Chris",
"last_name":"Mas"
},
"type":"self-help"
}
]
}
What is a good algorithm to convert it?
I am a C# person, I intend to use Newtonsoft.Json to parse the input JSON to a JObject, then iterate through all fields to check their keys and create nested JObjects. For arrays, I repeat the same process for every array item.
Do you have any better idea?
This is my solution for those who are interested.
public static string ConvertFlatJson(string input)
{
var token = JToken.Parse(input);
if (token is JObject obj)
{
return ConvertJObject(obj).ToString();
}
if (token is JArray array)
{
return ConvertArray(array).ToString();
}
return input;
}
private static JObject ConvertJObject(JObject input)
{
var enumerable = ((IEnumerable<KeyValuePair<string, JToken>>)input).OrderBy(kvp => kvp.Key);
var result = new JObject();
foreach (var outerField in enumerable)
{
var key = outerField.Key;
var value = outerField.Value;
if (value is JArray array)
{
value = ConvertArray(array);
}
var fieldNames = key.Split('.');
var currentObj = result;
for (var fieldNameIndex = 0; fieldNameIndex < fieldNames.Length; fieldNameIndex++)
{
var fieldName = fieldNames[fieldNameIndex];
if (fieldNameIndex == fieldNames.Length - 1)
{
currentObj[fieldName] = value;
continue;
}
if (currentObj.ContainsKey(fieldName))
{
currentObj = (JObject)currentObj[fieldName];
continue;
}
var newObj = new JObject();
currentObj[fieldName] = newObj;
currentObj = newObj;
}
}
return result;
}
private static JArray ConvertArray(JArray array)
{
var resultArray = new JArray();
foreach (var arrayItem in array)
{
if (!(arrayItem is JObject))
{
resultArray.Add(arrayItem);
continue;
}
var itemObj = (JObject)arrayItem;
resultArray.Add(ConvertJObject(itemObj));
}
return resultArray;
}

Get string from another string array if value matches

String Array 1: (In this format: <MENU>|<Not Served?>|<Alternate item served>)
Burger|True|Sandwich
Pizza|True|Hot Dog
String Array 2: (Contains Menu)
Burger
Pizza
Grill Chicken
Pasta
I need the menu is served or any alternate item served for that particular item.
Code:
for(int i = 0; i < strArr2.Length; i++)
{
if(strArr2.Any(_r => _r.Split('|').Any(_rS => _rS.Contains(strArr1[i]))))
{
var menu = strArr2[i];
var alternate = ? // need to get alternate item
}
}
As I commented in the code, how to get the alternate item in that string array? Please help, thanks in advance.
P.S: Any help to trim if condition is also gladly welcome.
Instead of any, you may use Where to get the value matching.
#Markus is having the detailed answer, I am just using your code to find a quick fix for you.
for(int i = 0; i < strArr2.Length; i++)
{
if(strArr2.Any(_r => _r.Split('|').Any(_rS => _rS.Contains(strArr1[i]))))
{
var menu = strArr2[i];
var alternate = strArr2.Where(_rs => _rs.Split('|').Any(_rS => _rS.Contains(strArr1[i]))).First().Split('|').Last();
}
}
In order to simplify your code, it is a good idea to better separate the tasks. For instance, it will be much easier to handle the contents of string array 1 after you have converted the contents into objects, e.g.
class NotServedMenu
{
public string Menu { get; set; }
public bool NotServed { get; set; }
public string AlternateMenu { get; set; }
}
Instead of having an array of strings, you can read the strings to a list first:
private IEnumerable<NotServedMenu> NotServedMenusFromStrings(IEnumerable<string> strings)
{
return (from x in strings select ParseNotServedMenuFromString(x)).ToArray();
}
private NotServedMenu ParseNotServedMenuFromString(string str)
{
var parts = str.Split('|');
// Validate
if (parts.Length != 3)
throw new ArgumentException(string.Format("Unable to parse \"{0}\" to an object of type {1}", str, typeof(NotServedMenu).FullName));
bool notServedVal;
if (!bool.TryParse(parts[1], out notServedVal))
throw new ArgumentException(string.Format("Unable to read bool value from \"{0}\" in string \"{1}\".", parts[1], str));
// Create object
return new NotServedMenu() { Menu = parts[0],
NotServed = notServedVal,
AlternateMenu = parts[2] };
}
Once you can use the objects, the subsequent code will be much cleaner to read:
var notServedMenusStr = new[]
{
"Burger|True|Sandwich",
"Pizza|True|Hot Dog"
};
var notServedMenus = NotServedMenusFromStrings(notServedMenusStr);
var menus = new[]
{
"Burger",
"Pizza",
"Grill Chicken",
"Pasta"
};
var alternateMenus = (from m in menus join n in notServedMenus on m equals n.Menu select n);
foreach(var m in alternateMenus)
Console.WriteLine("{0}, {1}, {2}", m.Menu, m.NotServed, m.AlternateMenu);
In this sample, I've used a Linq join to find the matching items.
You could do something like that
string[] strArr1 = { "Burger|True|Sandwich", "Pizza|True|Hot Dog" };
string[] strArr2 = { "Burger", "Pizza", "Grill Chicken", "Pasta" };
foreach (string str2 in strArr2)
{
string str1 = strArr1.FirstOrDefault(str => str.Contains(str2));
if (str1 != null)
{
string[] splited = str1.Split('|');
string first = splited[0];
bool condition = Convert.ToBoolean(splited[1]);
string second = splited[2];
}
}

How do I get the matching values in a csv file?

So I have a .csv file with 2 columns looking a bit like this:
01,23
02,45
03,178
etc.
Now I want to read the csv file, give it a value for the first column, and get the corresponding value from the second column back. (so say I give it a value of 03, it should give me 178 back)
Here's the code I've got so far but what should go in the if statement?
public int CalculateNextLevel(int current_xp, int current_lvl)
{
var reader = new StreamReader(File.OpenRead(#"C:\Users\Lennart\Desktop\Legends of Raymere\Files\Lvl.csv"));
List<int> levels = new List<int>();
List<int> exp = new List<int>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
levels.Add(Convert.ToInt32(values[0]));
exp.Add(Convert.ToInt32(values[1]));
foreach (int level in levels)
{
if (current_lvl == level)
{
}
}
}
return XP_to_nxt_lvl;
}
You can use a Dictionary instead
var expValues = new Dictionary<int, int>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
expValues.Add(Convert.ToInt32(values[0]), Convert.ToInt32(values[1]));
}
// Retrieve value based on level
if (expValues.ContainsKey(3))
{
int level03Exp = expValues[3];
}
try this
static void Main(string[] args)
{
string key = "03";
GetValue(key);
}
private static int GetValue(string key)
{
var lines = File.ReadAllLines("test.txt");
var dictonary = lines.ToDictionary(dict =>
{
return dict.Split(',')[0];
});
int valInt = int.Parse(dictonary[key].Split(',')[1]);
return valInt;
}
You should use Linq like this :
foreach (int level in levels)
{
if (current_lvl == level)
{
XP_to_nxt_lvl = exp[levels.IndexOf(level)];
}
}
You could use LINQ and a Lookup<TKey, TValue> which allows duplicate keys but is similar to a Dictionary. If the key(level) is not present you get an empty collection of xp's:
private ILookup<int, int> LevelLookup = null;
public void LoadAllLevels()
{
LevelLookup = File.ReadLines(#"C:\Temp\Lvl.csv")
.Select(l => l.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
.Select(cols =>
{
int level = 0, xp = 0;
bool validLine = cols.Length == 2;
if(validLine)
validLine = int.TryParse(cols[0].Trim(), out level);
if(validLine)
validLine = int.TryParse(cols[1].Trim(), out xp);
return new{ level, xp, validLine };
})
.Where(x => x.validLine)
.ToLookup(x => x.level, x => x.xp);
}
public int? CalculateNextLevel(int current_xp, int current_lvl)
{
int? nextLevel = null;
var xps = LevelLookup[current_lvl];
if (xps.Any())
nextLevel = xps.First();
return nextLevel;
}
You only need to initialize it once or when the file changed via LoadAllLevels().
For example:
LoadAllLevels();
int level3 = 3;
int level4 = 4;
int? xp3 = CalculateNextLevel(100,level3);
int? xp4 = CalculateNextLevel(150,level4);
I have used nullables to differentiate between XP=0 and a level has yet no defined xp.
bool hasLev4XP = xp4.HasValue;
if(hasLev4XP)
{
int lev4XpNeeded = xp4.Value;
}
If the level is guaranteed to be unique you could also use ToDictionary to create a dictionary and use similar code as above.

Convert custom delimited string to List<myClass>?

How do I turn this:
string x = "key:value|key:value|key:value|key:value";
into this?
List<myClass> listObj;
myClass definition:
public class myClass
{
public string keyName { get; set; }
public string keyValue { get; set; }
}
There has to be a way to do it using LINQ or something :)
thanks in advance!
* NOTE *
I should add I know how to do this splitting it and looping through it, but there has to be a better way :)
This will require separate ToList() call, but I like query syntax for its declarative nature:
from s in x.Split('|')
let parts = s.Split(':')
select new myClass {
keyName = parts[0],
keyValue = parts[1]
}
Or you can use fluent syntax:
x.Split('|')
.Select(s => {
var parts = s.Split(':');
return new myClass {
keyName = parts[0],
keyValue = parts[1]
};
}).ToList()
Well, since you really wanted to avoid splitting and looping...
public List<MyClass> Parse(string base, string workingName, string workingValue,
bool processingName = true,
List<MyClass> workingList = null, int index = 0)
{
if (workingList == null)
workingList = new List<MyClass>();
if (index >= base.Length)
{
return workingList;
}
if (base[index] = '|')
{
workingList.Add(new MyClass { keyName = workingName, keyValue = workingValue });
return Parse(base, "", "", true, workingList, index + 1);
}
else if (base[index] = ':')
{
return Parse(base, workingName, "", false, workingList, index + 1);
}
else if (processingName)
{
return Parse(base, workingName + base[index], "", processingName, workingList, index + 1);
}
else
{
return Parse(base, workingName, workingValue + base[index], processingName, workingList, index + 1);
}
}
But please, for the love of whatever you hold dear, don't do anything even remotely resembling that (and yes, this is untested, hand-written code, so there are probably errors - just making a joke about avoiding things).
Do this if you prefer to use your custom class instead of Dictionary
var result = from y in x.Split('|')
let obj = y.Split(':')
select new myClass{keyName = obj[0], keyValue = obj[1]};
var list = result.ToList();

Processing a C# Dictionary using LINQ

How do I write the "// Display using Foreach" loop implementation using LINQ Lambda Expression / Statemene Expression?
I want to simplify my development and avoid nested foreach loops as much as possible. I am trying to include more logic with in the second foreach statement and I want to use Lambda / Statement expression.
internal class Program
{
internal class Country
{
public string CountryName { get; set; }
public int CountryCode { get; set; }
}
static void Main(string[] args)
{
List<Country> countries = new List<Country>()
{
new Country{CountryName = "India", CountryCode=1},
new Country{CountryName = "Andaman Nicobar", CountryCode=1},
new Country{CountryName = "United States of America", CountryCode=2},
new Country{CountryName = "Alaska", CountryCode=2},
new Country{CountryName = "Hawaii", CountryCode=2},
new Country{CountryName = "United Kingdom", CountryCode=3},
new Country{CountryName = "Australia", CountryCode=4}
};
Dictionary<int, List<Country>> countriesDictionary = new Dictionary<int, List<Country>>();
foreach (Country country in countries)
{
if (!countriesDictionary.ContainsKey(country.CountryCode))
countriesDictionary.Add(country.CountryCode, new List<Country>());
countriesDictionary[country.CountryCode].Add(country);
}
// Display using Foreach
foreach (int key in countriesDictionary.Keys)
{
List<Country> dCountries = countriesDictionary[key];
foreach (Country country in dCountries)
{
if (country.CountryCode.Equals(key))
{
Console.WriteLine(country.CountryName);
}
}
Console.WriteLine();
}
Console.ReadLine();
}
Please suggest.
This is another alternative:
countriesDictionary.ToList().ForEach
(
pair =>
{
pair.Value.ForEach(country => Console.WriteLine(country.CountryName));
Console.WriteLine();
}
);
Also, this one based on Romoku's Answer(the answer was removed):
var countriesDictionary = countries.ToLookup(x => x.CountryCode, x => x);
foreach(var countryGroup in countriesDictionary)
{
foreach(var country in countryGroup)
{
Console.WriteLine(country.CountryName);
}
Console.WriteLine();
}
If you want to group countries by code, then you don't need two dictionaries. Use Enumerable.GroupBy
foreach(var codeGroup in countries.GroupBy(c => c.CountryCode))
{
foreach(var country in codeGroup)
Console.WriteLine(country.CountryName);
Console.WriteLine();
}
Or just use your countriesDictionary (it already has countries grouped by code):
foreach(var kvp in countriesDictionary)
{
foreach(var country in kvp.Value)
Console.WriteLine(country.CountryName);
Console.WriteLine();
}
Though you probably have enough answers for the time being, this LINQ will compress your dictionary into a list of country names, allowing an easy foreach to display them.
List<string> countryNames = countriesDictionary.SelectMany(
pair=>pair.Value.Where(
country=>country.CountryCode == pair.Key
).Select(x=>x.CountryName)).ToList();
foreach (var name in countryNames)
Console.WriteLine(name);
But the way your Dictionary is set up, the key should always match the country codes in the value, correct?
Or just make it very easy... as already mentioned, you're already grouping by Country code...
foreach (var country in countriesDictionary.SelectMany(pair => pair.Value))
{
Console.WriteLine(country.CountryName);
}
One way could be like this,avoiding the first foreach with GroupBy,the just 1 foreach logic to print each country name with specified code:
Dictionary<int, List<Country>> countriesDictionary = countries.GroupBy(g => g.CountryCode).ToDictionary(k => k.Key, k => k.ToList());
foreach (int key in countriesDictionary.Keys)
{
Console.WriteLine("****Countries with code {0}****",key);
int count = 0;
while (count < countriesDictionary[key].Count)
{
Console.WriteLine(countriesDictionary[key][count].CountryName);
count++;
}
Console.WriteLine();
}
Console.ReadLine();
I will use a extension to do it.
static class CountryExtension
{
public static void WriteCountriesGroupyCountryCode(this IEnumerable<Country> list)
{
int currentCountry=int.MinValue;
list.OrderBy(c => c.CountryCode).ThenBy(c=>c.CountryName).ToList().ForEach(c =>
{
if (currentCountry == int.MinValue)
{
currentCountry = c.CountryCode;
}
else if (currentCountry != c.CountryCode)
{
Console.WriteLine();
currentCountry = c.CountryCode;
}
Console.WriteLine(c.CountryName);
});
}
}

Categories

Resources