I am web-scraping some data and trying to write the scraped data to a json file using C# newtonsoft.Json
I get stuck when writing a foreach in my .ToDictionary function as well as not being able to ++ an index into my .ToDictionary function.
My class:
public class JsonParametersData
{
public bool required { get; set; }
public bool list { get; set; }
public List<string> options { get; set; }
}
My arrays
var jsonData = new List<Dictionary<string, Dictionary<string, JsonParametersData>>>();
var moduleParameters = new List<string>();
var parameterOptionsArray = new List<List<string>>();
var parameterOptions = new List<string>();
var requiredArray = new List<bool>();
var listArray = new List<bool>();
string moduleName = item.Attributes["href"].Value.Replace("_module.html", "");
The code which is commented shows what I am trying to do.
int index = 0;
jsonData.Add(new Dictionary<string, Dictionary<string, JsonParametersData>>()
{
{
moduleName,
moduleParameters
.ToDictionary(n => n,
n => new JsonParametersData
{
required = requiredArray[index],
list = listArray[index],
options = new List<string>() { "option1", "option2" },
/*
foreach (var parameteroption in parameterOptionsArray[index])
{
options.Add(parameteroption);
}
index++;
*/
})
}
});
string json = JsonConvert.SerializeObject(jsonData.ToArray());
//write string to file
System.IO.File.WriteAllText(#"path", json);
Your parameterOptionsArray is not an Array, but a List of lists.
The thing is that parameterOptionsArray[index] is a List, not a string. So you should use AddRange() instead of Add().
parameterOptionsArray.Foreach(parameteroption => options.AddRange(parameteroption));
As I´ve written in the comments you can make only assignments in an object-initializer. Thus the following is allowed:
var a = new { MyMember = anInstance }
whilst this is not:
var a = new { MyMember = anInstance, anInstance.DoSomething() };
That´s one of those cases where you should not use Linq at all, as it leads to more confusion than it helps. Instead use a good old-styled loop:
int index = 0;
var innerDict = new Dictionary<string, JsonParametersData>();
foreach(var name in moduleParameters)
{
innerDict[name] = new JsonParametersData
{
required = requiredArray[index],
list = listArray[index],
options = new List<string>() { "option1", "option2" },
}
innerDict[name].Options.AddRange(parameterOptionsArray[index]);
index++;
}
var dict = new Dictionary<string, Dictionary<string, JsonParametersData>>();
dict[moduleName] = innerDict;
jsonData.Add(dict);
string json = JsonConvert.SerializeObject(jsonData.ToArray());
You appear to have a jagged array in parameterOptionsArray. You can make use of SelectMany here. Perhaps following sample can help:
string[][] parameterOptionsArray = new string[2][];
parameterOptionsArray[0] = new string[2];
parameterOptionsArray[0][0] = "1";
parameterOptionsArray[0][1] = "2";
parameterOptionsArray[1] = new string[2];
parameterOptionsArray[1][0] = "3";
parameterOptionsArray[1][1] = "4";
var testing = new {options = parameterOptionsArray.SelectMany(x => x).ToList()};
testing.options.ForEach(x => Console.WriteLine(x));
Related
HI i am writing a c# program where i need to populate a array based on a look up table and set of string arrays with metadata. My lookup table looks like this (Table with key: transmitter, value: Array of receiver)
{
LED1: ["px1","px2","px3"],
LED2: ["px4","px5","px6"]
}
and my meta arrays looks like this (It is dynamic. Just an example. This comes as a response from DB query.)
var transmitters = new string[] { "LED1", "LED2" };
var receivers = new string[] { "px1", "px2", "px3", "px4", "px5", "px6" };
My requirement is
If the transmitter LED1 or LED2 (or any other transmitter) is present in the lookup table, the value of the transmitter (ie ["px1","px2","px3"]) has to be compared with the receiver which are present in the lookup and led has to be marked yellow.
Orphan tranmitter or/ receiver has to be marked red.
Example
LookUp
{
LED1: ["px1", "px2", "px3"],
LED2: ["px5", "px8"]
}
Tranmitters and receivers
var transmitters = new string[] { "led1", "led2" };
var receivers = new string[] { "px1", "px2", "px3", "px4", "px5", "px6" };
the result should be a list as
led1-yellow
px1-yellow
px2-yellow
px3-yellow
led2-yellow
px5-yellow
px4-red
px6-red.
I have written code that works
public class Program
{
public static void Main()
{
var transmitters = new string[] { "led1", "led2" };
var receivers = new string[] { "px1", "px2", "px3", "px4", "px5", "px6" };
var lookup = new Dictionary<string, string[]>() { { "led1", new string[] { "px1", "px2", "px3" } }, { "led2", new string[] { "px5", "px8" } } };
var blocks = new List<Block>();
var blocksTracker = new List<string>();
foreach (var transmitter in transmitters)
{
if (lookup.ContainsKey(transmitter))
{
var receiverLookup = lookup[transmitter];
var intersection = receivers.Intersect(receiverLookup).ToArray();
if (intersection.Length > 0)
{
blocks.Add(new Block() { Id = transmitter, status = "yellow" });
blocksTracker.Add(transmitter);
foreach (var receiver in intersection)
{
blocks.Add(new Block() { Id = receiver, status = "yellow" });
blocksTracker.Add(receiver);
}
}
else
{
blocks.Add(new Block() { Id = transmitter, status = "red" });
blocksTracker.Add(transmitter);
}
}
}
}
}
I am new to c# and i wanted to know if there is a better way of doing this. Please help. You can see the working fiddle Here
this is my .csv file:
Apple,rose,tiger
Mango,lily,cheetah
Banana,sunflower,lion
Apple,marigold,cat
input: Mango (i write it in the text box)
desired output:
Flower = lily; Animal = cheetah
similarly,
input: Apple
desired output:
Flower = rose,marigold; Animal = tiger,cat
this is the code i have written:
private void button1_Click(object sender, EventArgs e)
{
using (var reader = new StreamReader(#"C:\asp_net\abc.csv"))
{
List<string> listA = new List<string>();
List<string> listB = new List<string>();
List<string> listC = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
listA.Add(values[0]);
listB.Add(values[1]);
listC.Add(values[2]);
}
string checkThis = obj.SearchSenSig(textBox1.Text);
if (listA.Any(checkThis.Contains))
{
int count = listA.Where(x => x.Equals(checkThis)).Count();
if (count == 1)
{
int index = listA.IndexOf(checkThis);
var firstItem = listB.ElementAt(index);
var secondItem = listC.ElementAt(index);
MessageBox.Show(String.Format("receiver = {0}, url = {1}", firstItem, secondItem));
}
else
{
foreach (string item in listA)
{
int i = listA.IndexOf(item);
bool result = item.Equals(checkThis);
if (result)
{
List<string> myCollection1 = new List<string>();
myCollection1.Add(listB.ElementAt(i));
string firstItem = string.Join(",", myCollection1);
List<string> myCollection2 = new List<string>();
myCollection2.Add(listC.ElementAt(i));
string secondItem = string.Join(",", myCollection2);
MessageBox.Show(String.Format("Flower = {0}, Animal = {1}", firstItem, secondItem));
}
}
}
}
else
{
MessageBox.Show("The search element does not exists.");
}
}
Still i am not getting the desired output. Please help.
Instead of creating a different list for each column, create a single class to hold an entire row data:
class Data // I'll bet you can find a better name for this class...
{
public string Fruit {get;set;}
public string Flower {get;set;}
public string Animal {get;set;}
}
and populate a list of this class:
private List<Data> data = new List<Data>(); // note: this is a field, not a local variable.
Populating this list should be done only once, in the constructor or in the form_load event:
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
data.Add(
new Data()
{
Fruit = values[0],
Flower = values[1],
Animal = values[2]
}
);
}
Now all you have to do in the button_click event handler is get all the items corresponding to your search string. Assuming you are only searching for fruits using the FindAll method and display the results:
var result = data.FindAll(d => d.Fruit == searchString);
This will return a list of Data where the Fruit property contains the same string as searchString. use linq and string.Join to format the results into a string:
var resultString = $"Flower = {string.Join(",", result.Select(r => r.Flower))}; Animal = {string.Join(",", result.Select(r => r.Animal))}";
please tell me, how do I get the json like this:
dynamic packet = new ExpandoObject();
packet.type = "somethink";
packet.user = 12345;
packet.nets[0].amout = 123;
packet.nets[0].lower = 0;
packet.nets[1].amout = 345;
packet.nets[1].lower = 1;
string input = Newtonsoft.Json.JsonConvert.SerializeObject(packet);
Its not workig, error:
An unhandled exception of type "Microsoft.CSharp.RuntimeBinder.RuntimeBinderException" in System.Core.dll
For more information: "System.Dynamic.ExpandoObject" does not contain definitions of "nets"
Thanks.
It's the ExpandoObject who's a dynamic object. The rest of properties should be other ExpandoObject instances or regular objects, arrays, collections...
For example:
packet.nets = new[]
{
new { amount = 123, lower = 0 },
new { amount = 345, lower = 1 }
}
Or:
packet.nets = new[]
{
new Dictionary<string, int> { { "amount", 345 }, { "lower", 0 } },
new Dictionary<string, int> { { "amount", 123 }, { "lower", 1 } }
}
There're many other approaches, including the use of instances of concrete classes.
Firstly, you need create nets in packet object, like this:
packet.nets = new dynamic[2];
And initialize the objects in nets, if you want with `ExpandoObject too:
packet.nets[0] = new ExpandoObject();
packet.nets[1] = new ExpandoObject();
Then is done, complete code:
dynamic packet = new ExpandoObject();
packet.type = "somethink";
packet.user = 12345;
packet.nets = new dynamic[2];
packet.nets[0] = new ExpandoObject();
packet.nets[0].amout = 123;
packet.nets[0].lower = 0;
packet.nets[1] = new ExpandoObject();
packet.nets[1].amout = 345;
packet.nets[1].lower = 1;
string input = Newtonsoft.Json.JsonConvert.SerializeObject(packet);
You first need to declare nets. For example
packet.nets = new Dictionary<int, dynamic>();
Then you'll need to instantiate the instances of nets
packet.nets[0] = new {amount = 123, lower = 0};
The result being
dynamic packet = new ExpandoObject();
packet.type = "somethink";
packet.user = 12345;
packet.nets = new Dictionary<int, dynamic>();
packet.nets[0] = new { amount = 123, lower = 0 };
packet.nets[1] = new { amount = 345, lower = 1 };
I am trying to use this answer to dynamically add an OR operator to the WHERE clause in LINQ: https://stackoverflow.com/a/782350/1316683
var searchPredicate = PredicateBuilder.False<Songs>();
foreach(string str in strArray)
{
var closureVariable = str;
searchPredicate =
searchPredicate.Or(SongsVar => SongsVar.Tags.Contains(closureVariable));
}
var allSongMatches = db.Songs.Where(searchPredicate);
In my case Tags is a List<Tag>, not a property, I would like to do something like this:
searchPredicate.Or(SongsVar => SongsVar.Tags.Any().TagName.Contains(closureVariable));
Then I thought of something like this, which doesn't work:
searchPredicate.Or(x => x.Tags.Where(p => p.TagName.Contains(closureVariable )).Count() > 0);
Is this possible?
Yes, it should be possible:
var searchPredicate = PredicateBuilder.False<Songs>();
var strArray = new[] {"aa", "bb"};
foreach (string str in strArray) {
var closureVariable = str;
searchPredicate =
searchPredicate.Or(songsVar => songsVar.Tags.Any(tagVar => tagVar.TagName == closureVariable));
}
var songs = new List<Songs> {
new Songs {Tags = new List<Tag> {new Tag {TagName = "aa"}}},
new Songs {Tags = new List<Tag> {new Tag {TagName = "bb"}}},
new Songs {Tags = new List<Tag> {new Tag {TagName = "aa"}, new Tag {TagName = "cc"}}},
new Songs {Tags = new List<Tag> {new Tag {TagName = "cc"}, new Tag {TagName = "dd"}}}
};
var res = songs.Where(searchPredicate.Compile());
With Songs and Tag defined as:
public class Songs {
public List<Tag> Tags { get; set; }
}
public class Tag {
public string TagName { get; set; }
}
Then, res contains the first 3 records defined in songs as expected.
Is it possible to implicitly declare next Dictionary<HyperLink, Anonymous>:
{ urlA, new { Text = "TextA", Url = "UrlA" } },
{ urlB, new { Text = "TextB", Url = "UrlB" } }
so I could use it this way:
foreach (var k in dic)
{
k.Key.Text = k.Value.Text;
k.Key.NavigateUrl = k.Value.Url;
}
?
How about:
var dict = new[] {
new { Text = "TextA", Url = "UrlA" },
new { Text = "TextB", Url = "UrlB" }
}.ToDictionary(x => x.Url);
// or to add separately:
dict.Add("UrlC", new { Text = "TextC", Url = "UrlC" });
However, you could just foreach on a list/array...
var arr = new[] {
new { Text = "TextA", Url = "UrlA" },
new { Text = "TextB", Url = "UrlB" }
};
foreach (var item in arr) {
Console.WriteLine("{0}: {1}", item.Text, item.Url);
}
You only need a dictionary if you need O(1) lookup via the (unique) key.
Yes, but only with great workaround, and only within a method.
This is how you can do it:
static Dictionary<TKey, TValue> NewDictionary<TKey, TValue>(TKey key, TValue value)
{
return new Dictionary<TKey, TValue>();
}
public void DictRun()
{
var myDict = NewDictionary(new { url="a"},
new { Text = "dollar", Url ="urlA"});
myDict.Add(new { url = "b" }, new { Text = "pound", Url = "urlB" });
myDict.Add(new { url = "c" }, new { Text = "rm", Url = "urlc" });
foreach (var k in myDict)
{
var url= k.Key.url;
var txt= k.Value.Text;
Console.WriteLine(url);
Console.WriteLine(txt);
}
}
You can refer to this SO question for more info.