F# Object initialization with indexers - f#

C# allows the following object initialization syntax for classes with Item indexer:
using System.Text.Json.Nodes;
namespace JsonNodeFromObjectExample;
...
// Create a new JsonObject using object initializers.
var forecastObject = new JsonObject
{
["Date"] = new DateTime(2019, 8, 1),
["Temperature"] = 25,
["Summary"] = "Hot",
["DatesAvailable"] = new JsonArray(new DateTime(2019, 8, 1),
new DateTime(2019, 8, 2)),
["TemperatureRanges"] = new JsonObject
{
["Cold"] = new JsonObject
{
["High"] = 20,
["Low"] = -10
}
},
["SummaryWords"] = new JsonArray("Cool", "Windy", "Humid")
};
...
In F# I can
let forecastObject = JsonObject()
forecastObject["Date"] <- DateTime(2019, 8, 1)
forecastObject["Temperature"] <- 25
...
But I need to repeatedly write the object name, which is uglier, less succinct and more error prone.
A potentially equivalent syntax would be the one available for properties initialization:
let forecastObject = JsonObject(
["Date"] = DateTime(2019, 8, 1),
["Temperature"] = 25,
...
)
However it doesn't work with indexers like Item().
Does anyone know if there is an equivalent syntax in F# like there is in C#?

There's no equivalent syntax in F#, but if you're willing to use a little helper method, you could do this:
type JsonObject with
member this.Set(key : string, value) =
this[key] <- value
this
let forecastObject =
JsonObject()
.Set("Date", DateTime(2019, 8, 1))
.Set("Temperature", 25)
...

You can initialize it with a dict.
let forecastObject = JsonObject(dict<string, JsonNode>[
"Date", DateTime(2019, 8, 1);
"Temperature", 25
])

I often do this, which is not shorter, but sort of encapsulates the initialization of the thing.
let forecastObject =
let x = JsonObject()
x["Date"] <- DateTime(2019, 8, 1)
x["Temperature"] <- 25
x

Related

How to insert data and tables into an existing one?

I have a trouble inserting new rows and tables into an already existing one.
Lets call the source SourceFile.lua and its simplified contents:
SourceFile = {};
SourceFile.list = {
BrandName1 = {
number = 10,
products = {
"Product1", 3,
"Product2", 4,
"Product3", 7,
},
},
BrandName2 = {
number = 5,
products = {
"Product1", 10,
"Product2", 3,
"Product3", 6,
},
},
-- and so on
}
I want to do something like this:
require 'SourceFile'
local myData = {
BrandName2 = { -- add new products for the existing brand
products = {
"Product4", 2,
},
},
MyBrandName1 = { -- add new brand
number = 12,
products = {
"MyProduct1", 21,
"MyProduct2", 95,
},
},
-- and so on
}
table.insert(SourceFile.list, myData)
However there's something wrong in my code and I get the following result (printed with inspect):
{
list = { {
BrandName2 = {
products = { "Product4", 2 }
},
MyBrandName1 = {
number = 12,
products = { "MyProduct1", 21, "MyProduct2", 95 }
}
},
BrandName1 = {
number = 10,
products = { "Product1", 3, "Product2", 4, "Product3", 7 }
},
BrandName2 = {
number = 5,
products = { "Product1", 10, "Product2", 3, "Product3", 6 }
}
}
}
What am I doing wrong?
I'm new to lua and pretty sure that it's something obvious, but not for me. Please, help me.
Addition
After these answers I've also found a way to insert new brand names one by one:
SourceFile.list.MyBrandName1 = {
number = 12,
products = {
"MyProduct1", 21,
"MyProduct2", 95,
},
}
This does not fully answer my question, but might be useful to someone new to lua.
table.insert adds its second argument to an array (its first argument). Your SourceFile.list is only supposed to have string keys, so it can't work as an array. You'll need a recursive function to merge the data from one table into the other:
local function meld(data, newData)
for k, v in pairs(newData) do
local oldValue = data[k]
if type(oldValue) ~= 'table' or type(v) ~= 'table' then
-- One of the values is not a table, so let's clobber the old value.
data[k] = v
else
-- Both are tables.
meld(oldValue, v)
end
end
end
meld(SourceFile.list, myData)
You are pushing a table of brandnames into a list of brandnames.
Which makes it a list of brandnames + table with brandnames.
table.insert(SourceFile.list, myData)
This inserts myData to SourceFile.list. myData is a table with brandnames.
SourceFile.list is also a table with brandnames.
List in list.
You have 2 choices to solve this:
Insert each brandname separately
Make a function to merge contents of myData to SourceFile.list

Dynamically building subtables in a table

I'm trying to figure out how to dynamically build a series of sub-tables inside a lua table. For example
function BuildsubTable()
local oTable = {}
local name = {"P1","P2"}
for i = 1, 2 do
oTable[i] = {name = name[i], "x" = i + 2, "y" = i + 1}
end
return oTable
end
expected output:
oTable = {
{name = "P1", "x"=3, "y"=2},
{name = "P2", "x"=4, "y"=3}
}
Which obviously doesn't work, but you get the idea of what I'm trying to do. This is a rather simple task but in LUA 5.3 this is proving to be difficult. I cannot find a good example of building a table in this manner. Any solutions or other ideas would be appreciated. In Python I would use a class or a simple dictionary.
Your problem is that you quote the string indices. The generic syntax for declaring a table key inside the table constructor is [<key>] = <value>, for example, [20] = 20 or ["x"] = i + 2.
A shorthand for ["<key>"] = <value>, that is, for string indices that are valid variable names, you can write <key> = <value>, for example x = i + 2.
In your code you use a mix of both and write { ..., "x" = i + 2, ... }. A quick google search shows me that in Python, which you mention, you quote string keys in dictionaries, so you probably mixed that up with Lua?
EDIT: I noticed this a bit late, but you can also use ipairs to iterate the first table and table.insert to insert values:
function BuildsubTable()
local oTable = {}
local name = {"P1","P2"}
for i,name in ipairs(name) do
table.insert(oTable, {name = name, "x" = i + 2, "y" = i + 1})
end
return oTable
end
Use
oTable[i] = {name = name[i], x = i + 2, y = i + 1}
DarkWiiPlayers & lhf's answers are the proper way.
But here is how you can fix your current code if you intend to use a string as a key
function BuildsubTable()
local oTable = {}
local name = {"P1","P2"}
for i = 1, 2 do
oTable[i] = {name = name[i], ["x"] = i + 2, ["y"] = i + 1}
end
return oTable
end
Output
{
[1] = { ['name'] = 'P1', ['x'] = 3, ['y'] = 2},
[2] = { ['name'] = 'P2', ['x'] = 4, ['y'] = 3}
}

Group users by age for stats

first thing first my question is very similar to this one. In fact, it's the same thing, except that I need to group every user like this below:
Everyone under 12 (exclusive)
Then, from 12 - 19
Then, from 20 - 29
...
More than 80 (inclusive)
Based on the answer from dasblinkenlight in the other question, I was able to do:
var ageStats = vModel
.GroupBy(l => 10 * (l.Age / 10))
.OrderBy(x => x.Key)
.Select(g => new
{
Name = g.Key,
Count = g.Select(l => l.Age).Count()
}).ToList();
For a result set of :
0-9
10-19
20-29
...
So what should I do to accomplish the pattern I have to ?
Thank you very much !!
var ages = new[] { 12, 19, 29, 80 };
var grouped = ages.Select(r => new {
Name = r,
Count = vModel.Count(x => x.Age >= r)
});
try this, but i don't know the performance
var ages = new int[12, 19, 29, 80];
var func = new Func<int, int>(a=>{
for(var i = 0; i<ages.Length; i++){
if(a<ages[i])
continue;
return i;
}
return 0;
});
vModel.GroupBy(m=>func(m.Age))....
You could use approach mentioned here and use this code:
var ages = new List<int> { 12, 19, 29, 39, 49, 59, 69, 80, int.MaxValue};
var categories = vModel.GroupBy(item => ages.FirstOrDefault(ceil => ceil >= item));

casting a List<object> into NancyFx response

I am trying to build an Nancy OData support app using LinqToQuerystring. I got below sample code. it is working for any query url like:
http:/test/?$filter=Recommended eq true
Get["/test"] = _ =>
{
var dict = (IDictionary)Request.Query.ToDictionary();
new List<Movie>
{
new Movie
{
Title = "Matrix (The)",
ReleaseDate = new DateTime(1999, 3, 31),
DurationInMinutes = 136,
MetaScore = 73,
Director = "Wachowski Brothers",
Recommended = true
},
new Movie
{
Title = "There and Back Again, An Unexpected Journey",
ReleaseDate = new DateTime(2012, 12, 14),
DurationInMinutes = 169,
MetaScore = 58,
Director = "Peter Jackson",
Recommended = false
}
}.AsQueryable()
.LinqToQuerystring(dict);
return dict;
}
You can solve this by calling ToDictionary first.
i.e
var dict = (IDictionary<string, object>) Request.Query.ToDictionary();
...
.LinqToQuerystring(dict);
This is probably because of the way LinqToQuerystring handles Dictionary under the hood, outputting them in an intermediate window causes:
(IDictionary<string, object>) Request.Query
{Nancy.DynamicDictionary}
[Nancy.DynamicDictionary]: {Nancy.DynamicDictionary}
Keys: Count = 2
Values: Count = 2
(IDictionary<string, object>) Request.Query.ToDictionary()
Count = 2
[0]: {[one, one]}
[1]: {[two, 2]}
Edit:
Based on your comment I assume you want to ALWAYS return JSON.
If that's the case the way you would do that is to return:
return Response.AsJson(dict);
This will serialize the dictionary as JSON for you.

Shorten array in actionscript?

How can I make this code shorter, is there any way? I feel there's too much repeating..
var bokstaver1:Array = new Array("a", "b", "c");
var bokstaver2:Array = ["d","e","f"];
var bokstaver:Array = New Array();
bokstaver[0] = "b";
bokstaver[1] = "i";
bokstaver[2] = "l";
bokstaver[3] = "l";
bokstaver[4] = "e";
I'm all new here so if this is not a way to ask a question on here please don't hasten insults.
You can do it in this easy way:
var bokstaver:Array = "bille".split("");
trace(bokstaver); // outputs: b,i,l,l,e

Resources