I have currently run into an issue while toying with Lua and the alien module to use Win32 API and such from Lua scripts. So far I have only had a single issue with alien which is with the use of API that use certain structures such as CreateFontIndirect.
For example:
HFONT CreateFontIndirectA( const LOGFONT& lplf );
LOGFONT:
typedef struct tagLOGFONT {
LONG lfHeight;
LONG lfWidth;
LONG lfEscapement;
LONG lfOrientation;
LONG lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
TCHAR lfFaceName[LF_FACESIZE];
}LOGFONT, *PLOGFONT;
The issue lies with the font face name. I cannot get Lua to keep a string inside the structure itself, it always pushes a pointer into the structure. So there is no way, that I can figure out, to be able to use this API purely from Lua.
This is what I got working to a point:
LOGFONT = alien.defstruct {
{ 'lfHeight', 'long' },
{ 'lfWidth', 'long' },
{ 'lfEscapement', 'long' },
{ 'lfOrientation', 'long' },
{ 'lfWeight', 'long' },
{ 'lfItalic', 'byte' },
{ 'lfUnderline', 'byte' },
{ 'lfStrikeOut', 'byte' },
{ 'lfCharSet', 'byte' },
{ 'lfOutPrecision', 'byte' },
{ 'lfClipPrecision', 'byte' },
{ 'lfQuality', 'byte' },
{ 'lfPitchAndFamily', 'byte' },
{ 'lfFaceName', 'string' } -- This line isn't working properly.
}
gdi32 = alien.load( "gdi32.dll" )
gdi32.CreateFontIndirectA:types {
ret = 'long',
abi = 'stdcall',
'pointer'
}
An example to call it:
local lf = LOGFONT:new()
lf.lfHeight = 14
lf.lfWidth = 0
lf.lfEscapement = 0
lf.lfOrientation = 0
lf.lfWeight = 400
lf.lfItalic = 0
lf.lfUnderline = 0
lf.lfStrikeOut = 0
lf.lfCharSet = 0
lf.lfOutPrecision = 0
lf.lfClipPrecision = 0
lf.lfQuality = 0
lf.lfPitchAndFamily = 0
lf.lfFaceName = 'Terminal'
local hFont = gdi32.CreateFontIndirectA( lf() )
Debugging my application that runs my script shows that the api is being called properly, everything is passed properly except the font face. I've tried various different methods to get it working but I cant get it to go as needed.
Any tips on fixing this without hard-coding anything else into the exe?
Alien's string type is for pointers to strings only, that is why your example is not working. Try this:
-- LF_FACESIZE = ? -- put the value of the LF_FACESIZE constant here
LOGFONT = alien.defstruct {
{ 'lfHeight', 'long' },
{ 'lfWidth', 'long' },
{ 'lfEscapement', 'long' },
{ 'lfOrientation', 'long' },
{ 'lfWeight', 'long' },
{ 'lfItalic', 'byte' },
{ 'lfUnderline', 'byte' },
{ 'lfStrikeOut', 'byte' },
{ 'lfCharSet', 'byte' },
{ 'lfOutPrecision', 'byte' },
{ 'lfClipPrecision', 'byte' },
{ 'lfQuality', 'byte' },
{ 'lfPitchAndFamily', 'byte' },
{ 'lfFaceName', 'char' }
}
LOGFONT.size = LOGFONT.size + LF_FACESIZE - 1 -- so Alien allocates enough space
-- for the whole array
function get_lfname(lf) -- gets the lfFaceName field as a Lua string
local out = {}
local offset = LOGFONT.offsets.lfFaceName
local buf = lf()
for i = offset, offset+LF_FACESIZE-1 do
local c = buf:get(i, "char")
if c ~= 0 then
out[#out+1] = string.char(c)
else
break
end
end
return table.concat(out)
end
function set_lfname(lf, s) -- sets the Lua string s as the lfFaceName
local offset = LOGFONT.offsets.lfFaceName
local buf = lf()
for i = 1, LF_FACESIZE do
if i <= #s then
buf:set(offset+i, string.byte(string.sub(s, i, i)), "char")
else
buf:set(offset+i, 0, "char")
break
end
end
end
Now just allocate an LOFGONF structure as usual, but use the get_lfname and set_lfname functions to work with the lfFaceName attribute:
local lf = LOGFONT:new()
lf.lfHeight = 14
lf.lfWidth = 0
lf.lfEscapement = 0
lf.lfOrientation = 0
lf.lfWeight = 400
lf.lfItalic = 0
lf.lfUnderline = 0
lf.lfStrikeOut = 0
lf.lfCharSet = 0
lf.lfOutPrecision = 0
lf.lfClipPrecision = 0
lf.lfQuality = 0
lf.lfPitchAndFamily = 0
set_lfname(lf, 'Terminal')
local hFont = gdi32.CreateFontIndirectA( lf() )
Tacking an array at the end is a common pattern for structures in C programming that I forgot. I am going to put direct support for it in the next version of Alien.
Thanks much for the response mascarenhas, this solution worked. I did have to adjust your set_lfname function though, as the offset+i-1 was misaligned and was overwriting the lfPitchAndFamily byte in the structure, removed the -1 and it works great. :)
Related
I have data
{
"id": 1000,
"price": "99,01USA",
},
{
"id": 1001,
"price": "100USA",
},
{
"id": 1002,
"price": "780USA",
},
{
"id": 1003,
"price": "20USA",
},
How I sort order by price (ASC , DESC)
You can alter it a little to parse price to integer and then sort it
You can create a dynamic sort function that sorts objects by their value that you pass:
function dynamicSort(property) {
var sortOrder = 1;
if(property[0] === "-") {
sortOrder = -1;
property = property.substr(1);
}
return function (a,b) {
/* next line works with strings and numbers,
* and you may want to customize it to your needs
*/
var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
return result * sortOrder;
}
}
So you can have an array of objects like this:
var People = [
{Name: "Name", Surname: "Surname"},
{Name:"AAA", Surname:"ZZZ"},
{Name: "Name", Surname: "AAA"}
];
...and it will work when you do:
People.sort(dynamicSort("Name"));
People.sort(dynamicSort("Surname"));
People.sort(dynamicSort("-Surname"));
Actually this already answers the question. Below part is written because many people contacted me, complaining that it doesn't work with multiple parameters.
Multiple Parameters
You can use the function below to generate sort functions with multiple sort parameters.
function dynamicSortMultiple() {
/*
* save the arguments object as it will be overwritten
* note that arguments object is an array-like object
* consisting of the names of the properties to sort by
*/
var props = arguments;
return function (obj1, obj2) {
var i = 0, result = 0, numberOfProperties = props.length;
/* try getting a different result from 0 (equal)
* as long as we have extra properties to compare
*/
while(result === 0 && i < numberOfProperties) {
result = dynamicSort(props[i])(obj1, obj2);
i++;
}
return result;
}
}
Which would enable you to do something like this:
People.sort(dynamicSortMultiple("Name", "-Surname"));
Subclassing Array
For the lucky among us who can use ES6, which allows extending the native objects:
class MyArray extends Array {
sortBy(...args) {
return this.sort(dynamicSortMultiple(...args));
}
}
That would enable this:
MyArray.from(People).sortBy("Name", "-Surname");
Background
I am trying to use protobuff for one of our apps, but I am having trouble understanding the protocol and I need help creating a .proto file.
Data
The data I need to encode is a list of maps, with the following structure:
[
{
"AwayTeam": "Osasuna",
"Date": "2017-05-07",
"Div": "SP1",
"FTAG": 1,
"FTHG": 4,
"FTR": "H",
"HTAG": 0,
"HTHG": 2,
"HTR": "H",
"HomeTeam": "Valencia",
"Season": 201617
},
{
"AwayTeam": "Osasuna",
"Date": "2016-02-27",
"Div": "SP2",
"FTAG": 1,
"FTHG": 0,
"FTR": "A",
"HTAG": 0,
"HTHG": 0,
"HTR": "D",
"HomeTeam": "Cordoba",
"Season": 201516
}
]
Each map has the following structure:
{
"AwayTeam": string, required: true
"Date": string, required: true
"Div": string, required: true
"FTAG": integer, required: true
"FTHG": integer, required: true
"FTR": string, required: true
"HTAG": integer, required: true
"HTHG": integer, required: true
"HTR": string, required: true
"HomeTeam": string, required: true
"Season": integer, required: true
}
Research
My goal is to create .proto file using proto3. So I decided to read the documentation for .proto3 files:
https://developers.google.com/protocol-buffers/docs/proto3#maps
But I was even more confused. According to the docs, I cannot have a map holding values of different types:
https://developers.google.com/protocol-buffers/docs/proto3#maps
For that I would need the equivalent of the JSON object type and check the docs for .struct.proto but that page doesn't mention anything about it.
Question
So I am rather lost here. How do I represent the mentioned data structure in a .proto?
Answer
Turns out that I don't actually need a map, a list of objects (messages) would suffice:
syntax = "proto3";
message Result {
string AwayTeam = 1;
string Date = 2;
string Div = 3;
int32 FTAG = 4;
int32 FTHG = 5;
string FTR = 6;
int32 HTAG = 7;
int32 HTHG = 8;
string HTR = 9;
string HomeTeam = 10;
int32 Season = 11;
}
message Response {
repeated Result results = 1;
}
I'm working in TextMate2, but this question may apply to other text editors as well.
My script is in R. I intend to use rmarkdown::render() on the script to create a "report".
The clever part of these reports is that they distinguish between the standard comment symbol in R (#), and the following:
#' indicates markdown, like in roxygen,
#+ indicates that a knitr code chunk will follow
I suck at editing TextMate2 bundles. I managed to get hotkeys set up to comment out lines with #' and #+, and to do it with proper indentation. Now, I wish I could edit my theme (which I designed in TextMate1) to make one of those "special" comments a different color.
I've edited the R bundle's language grammar (this is how the file starts):
{ patterns = (
{ name = 'comment.line.pragma-mark.r';
match = '^(#pragma[ \t]+mark)[ \t](.*)';
captures = {
1 = { name = 'comment.line.pragma.r'; };
2 = { name = 'entity.name.pragma.name.r'; };
};
},
{ begin = '(^[ \t]+)?(?=#)';
end = '(?!\G)';
beginCaptures = { 1 = { name = 'punctuation.whitespace.comment.leading.r'; }; };
patterns = (
{ name = 'comment.line.number-sign.r';
begin = '#';
end = '\n';
beginCaptures = { 0 = { name = 'punctuation.definition.comment.r'; }; };
},
);
},
And inserted the following into the middle, hoping it would let me specify a new scope for syntax highlighting:
# START MY STUFF
{ begin = '(^[ \t]+)?(?=#'')';
end = '(?!\G)';
beginCaptures = { 1 = { name = 'punctuation.whitespace.comment.leading.r'; }; };
patterns = (
{ name = 'comment.line.number-sign-tick.r';
begin = "#'";
end = '\n';
beginCaptures = { 0 = { name = 'punctuation.definition.comment.r'; }; };
},
);
},
# END MY STUFF
If it would help, I could provide the rest of the language grammar, but I'm not sure it's relevant here.
I tried to be more specific when redefining the comment in the theme (previous was just comment, which I changed to comment.line.number-sign.r). Here are (what I think are) the relevant lines of the theme:
{ name = 'Comment';
scope = 'comment.line.number-sign.r';
settings = {
fontStyle = 'italic';
foreground = '#279797';
};
},
{ name = 'Comment';
scope = 'comment.line.number-sign-tick.r';
settings = {
fontStyle = 'italic';
foreground = '#C5060B';
};
},
So far, I cannot achieve any difference in the syntax highlighting of a line that starts with # versus a line that starts with #'. I can get both to change, but no independently. Any help in figuring out how to achieve different syntax highlighting for those two would be great.
TextMate is preferring the first scope, comment.line.number-sign.r to your custom grammars. All I did is paste your code above my comment.line.number-sign.r definition, instead of after as you had indicated, and expanded upon your existing grammar/theme.
Here's what I've got:
In Bundle Editor-> R -> Language Grammars -> R
{ patterns = (
//default block
{ name = 'comment.line.pragma-mark.r';
match = '^(#pragma[ \t]+mark)[ \t](.*)';
captures = {
1 = { name = 'comment.line.pragma.r'; };
2 = { name = 'entity.name.pragma.name.r'; };
};
},
//your block
{ begin = '(^[ \t]+)?(?=#'')';
end = '(?!\G)';
beginCaptures = { 1 = { name = 'punctuation.whitespace.comment.leading.r'; }; };
patterns = (
{ name = 'comment.line.number-sign-tick.r';
begin = "#'";
end = '\n';
beginCaptures = { 0 = { name = 'punctuation.definition.comment.r'; }; };
},
);
},
//my block
{ begin = '(^[ \t]+)?(?=#\+)';
end = '(?!\G)';
beginCaptures = { 1 = { name = 'punctuation.whitespace.comment.leading.r'; }; };
patterns = (
{ name = 'comment.line.number-sign-plus.r';
begin = '#\+';
end = '\n';
beginCaptures = { 0 = { name = 'punctuation.definition.comment.r'; }; };
},
);
},
//default caption block
{ begin = '(^[ \t]+)?(?=#)';
end = '(?!\G)';
beginCaptures = { 1 = { name = 'punctuation.whitespace.comment.leading.r'; }; };
patterns = (
{ name = 'comment.line.number-sign.r';
begin = '#';
end = '\n';
beginCaptures = { 0 = { name = 'punctuation.definition.comment.r'; }; };
},
);
},
//...
And then, in my theme:
//...
{ name = 'Comment';
scope = 'comment.line.number-sign.r';
settings = {
fontStyle = 'italic';
foreground = '#279797';
};
},
{ name = 'Comment';
scope = 'comment.line.number-sign-tick.r';
settings = {
fontStyle = 'italic';
foreground = '#C5060B';
};
},
{ name = 'Comment';
scope = 'comment.line.number-sign-plus.r';
settings = {
fontStyle = 'italic';
foreground = '#ff00ff';//fix this color(!)
};
},
);
}
I don't use R, so I just Googled for a quick example with all 3 kinds of comments. Here's the file I used to test.
A screenshot of what I'm seeing:
Say I have a TypeScript enum, MyEnum, as follows:
enum MyEnum {
First,
Second,
Third
}
What would be the best way in TypeScript 0.9.5 to produce an array of the enum values? Example:
var choices: MyEnum[]; // or Array<MyEnum>
choices = MyEnum.GetValues(); // plans for this?
choices = EnumEx.GetValues(MyEnum); // or, how to roll my own?
This is the JavaScript output of that enum:
var MyEnum;
(function (MyEnum) {
MyEnum[MyEnum["First"] = 0] = "First";
MyEnum[MyEnum["Second"] = 1] = "Second";
MyEnum[MyEnum["Third"] = 2] = "Third";
})(MyEnum || (MyEnum = {}));
Which is an object like this:
{
"0": "First",
"1": "Second",
"2": "Third",
"First": 0,
"Second": 1,
"Third": 2
}
Enum Members with String Values
TypeScript 2.4 added the ability for enums to possibly have string enum member values. So it's possible to end up with an enum that look like the following:
enum MyEnum {
First = "First",
Second = 2,
Other = "Second"
}
// compiles to
var MyEnum;
(function (MyEnum) {
MyEnum["First"] = "First";
MyEnum[MyEnum["Second"] = 2] = "Second";
MyEnum["Other"] = "Second";
})(MyEnum || (MyEnum = {}));
Getting Member Names
We can look at the example immediately above to try to figure out how to get the enum members:
{
"2": "Second",
"First": "First",
"Second": 2,
"Other": "Second"
}
Here's what I came up with:
const e = MyEnum as any;
const names = Object.keys(e).filter(k =>
typeof e[k] === "number"
|| e[k] === k
|| e[e[k]]?.toString() !== k
);
Member Values
Once, we have the names, we can loop over them to get the corresponding value by doing:
const values = names.map(k => MyEnum[k]);
Extension Class
I think the best way to do this is to create your own functions (ex. EnumEx.getNames(MyEnum)). You can't add a function to an enum.
class EnumEx {
private constructor() {
}
static getNamesAndValues(e: any) {
return EnumEx.getNames(e).map(n => ({ name: n, value: e[n] as string | number }));
}
static getNames(e: any) {
return Object.keys(e).filter(k =>
typeof e[k] === "number"
|| e[k] === k
|| e[e[k]]?.toString() !== k
);
}
static getValues(e: any) {
return EnumEx.getNames(e).map(n => e[n] as string | number);
}
}
With TypeScript >= 2.4 you can define string enums:
enum Color {
RED = 'Red',
ORANGE = 'Orange',
YELLOW = 'Yellow',
GREEN = 'Green',
BLUE = 'Blue',
INDIGO = 'Indigo',
VIOLET = 'Violet'
}
JavaScript ES5 output:
var Color;
(function (Color) {
Color["RED"] = "Red";
Color["ORANGE"] = "Orange";
Color["YELLOW"] = "Yellow";
Color["GREEN"] = "Green";
Color["BLUE"] = "Blue";
Color["INDIGO"] = "Indigo";
Color["VIOLET"] = "Violet";
})(Color || (Color = {}));
Which is an object like this:
const Color = {
"RED": "Red",
"ORANGE": "Orange",
"YELLOW": "Yellow",
"GREEN": "Green",
"BLUE": "Blue",
"INDIGO": "Indigo",
"VIOLET": "Violet"
}
Thus, in the case of string enums, no need to filter things,
Object.keys(Color) and Object.values(Color) are enough:
const colorKeys = Object.keys(Color) as (keyof typeof Color)[];
console.log('colorKeys =', colorKeys);
// ["RED", "ORANGE", "YELLOW", "GREEN", "BLUE", "INDIGO", "VIOLET"]
const colorValues = Object.values(Color);
console.log('colorValues =', colorValues);
// ["Red", "Orange", "Yellow", "Green", "Blue", "Indigo", "Violet"]
colorKeys.map(colorKey => {
console.log(`color key = ${colorKey}, value = ${Color[colorKey]}`);
});
/*
color key = RED, value = Red
color key = ORANGE, value = Orange
color key = YELLOW, value = Yellow
color key = GREEN, value = Green
color key = BLUE, value = Blue
color key = INDIGO, value = Indigo
color key = VIOLET, value = Violet
*/
See online example on TypeScript playground
There is no concept of RTTI (runtime type information) in TypeScript (think: reflection) so in order to do this, knowledge of the transpiled JavaScript is required. So, assuming TypeScript 0.95:
enum MyEnum {
First, Second, Third
}
becomes:
var MyEnum;
(function(MyEnum) {
MyEnum[MyEnum["First"] = 0] = "First";
MyEnum[MyEnum["Second"] = 1] = "Second";
MyEnum[MyEnum["Third"] = 2] = "Third";
}
So, this is modeled as a regular object in javascript, where MyEnum.0 == "First" and MyEnum.First == 0. So, to enumerate all of the enum names, you need to get all properties that belong to the object and that are also not numbers:
for (var prop in MyEnum) {
if (MyEnum.hasOwnProperty(prop) &&
(isNaN(parseInt(prop)))) {
console.log("name: " + prop);
}
}
Ok, so now I've told you how to do it, I'm allowed to tell you this is a bad idea. You're not writing a managed language, so you can't bring these habits. It's still just plain old JavaScript. If I wanted to use a structure in JavaScript to populate some kind of choices list, I would use a plain old array. An enum is not the right choice here, pun intended. The goal of TypeScript is to generate idiomatic, pretty JavaScript. Using enums in this way does not preserve this goal.
You can add functions to get the names and indices of the enum:
enum MyEnum {
First,
Second,
Third
}
namespace MyEnum {
function isIndex(key):boolean {
const n = ~~Number(key);
return String(n) === key && n >= 0;
}
const _names:string[] = Object
.keys(MyEnum)
.filter(key => !isIndex(key));
const _indices:number[] = Object
.keys(MyEnum)
.filter(key => isIndex(key))
.map(index => Number(index));
export function names():string[] {
return _names;
}
export function indices():number[] {
return _indices;
}
}
console.log("MyEnum names:", MyEnum.names());
// Prints: MyEnum names: ["First", "Second", "Third"]
console.log("MyEnum indices:", MyEnum.indices());
// Prints: MyEnum indices: [0, 1, 2]
Note that you could just export the _names and _indices consts rather than exposing them through an exported function, but because the exported members are members of the enum it is arguably clearer to have them as functions so they are not confused with the actual enum members.
It would be nice if TypeScript generated something like this automatically for all enums.
I used the solution proposed by David Sherret and wrote an npm library you can use named enum-values...
Git: enum-values
// Suppose we have an enum
enum SomeEnum {
VALUE1,
VALUE2,
VALUE3
}
// names will be equal to: ['VALUE1', 'VALUE2', 'VALUE3']
var names = EnumValues.getNames(SomeEnum);
// values will be equal to: [0, 1, 2]
var values = EnumValues.getValues(SomeEnum);
A one-liner to get a list of entries (key-value objects/pairs):
Object.keys(MyEnum).filter(a=>a.match(/^\D/)).map(name=>({name, value: MyEnum[name] as number}));
enum MyEnum {
First, Second, Third, NUM_OF_ENUMS
}
for(int i = 0; i < MyEnum.NUM_OF_ENUMS; ++i) {
// do whatever you need to do.
}
If you want to associate strings values to your enum these methods don't works. To have a generic function you can do :
function listEnum(enumClass) {
var values = [];
for (var key in enumClass) {
values.push(enum[key]);
}
values.length = values.length / 2;
return values;
}
It's works because TypeScript will add keys in first step, and values in second step.
In TypeScript it's:
var listEnums = <T> (enumClass: any): T[]=> {
var values: T[] = [];
for (var key in enumClass) {
values.push(enumClass[key]);
}
values.length = values.length / 2;
return values;
};
var myEnum: TYPE[] = listEnums<TYPE>(TYPE);
joe's answer just made me realize that is much more easier to rely on the first N numeric keys than making more complex testings:
function getEnumMembers(myEnum): string[]
{
let members = []
for(let i:number = 0; true; i++) {
if(myEnum[i] === undefined) break
members.push(myEnum[i])
}
return members
}
enum Colors {
Red, Green, Blue
}
console.log(getEnumMembers(myEnum))
Iterating over an enum
String Enums are best used for this. Here is an example:
// This is a string enum
enum MyEnum {
First = 'First',
Second = 'Second',
Third = 'Third',
}
// An enum is a TS concept
// However his MyEnum compiles to JS object:
// {
// "First": "First",
// "Second": "Second",
// "Third": "Third"
// }
// Therefore we can get the keys in the following manner:
const keysArray = Object.keys(MyEnum);
for (const key of keysArray) {
console.log(key)
}
// [LOG]: "First"
// [LOG]: "Second"
// [LOG]: "Third"
A type-safe solution could be as follows:
enum Color {
Blue = 'blue',
Green = 'green'
}
enum MoreColor {
Yellow,
Red
}
function getEnumValues<T, K extends keyof T>(enumType: T): Array<T[K]> {
return getEnumKeys<T, K>(enumType).map((x) => enumType[x]);
}
function getEnumKeys<T, K extends keyof T>(enumType: T): Array<K> {
return Object.keys(enumType)
.filter((x) => Number.isNaN(Number(x)))
.map((x) => x as K);
}
// return type is Color[]
const colorValues = getEnumValues(Color); // ["blue", "green"]
// return type is MoreColor[]
const moreColorValues = getEnumValues(MoreColor); // [0, 1]
// return type is Array<"Blue" | "Green">
const colorKeys = getEnumKeys(Color); // ["Blue", "Green"]
// return type is Array<"Yellow" | "Red">
const moreColorKeys = getEnumKeys(MoreColor); // ["Yellow", "Red"]
But keep in mind that this solution does not force you to pass just enums to the function.
for nodejs:
const { isNumber } = require('util');
Object.values(EnumObject)
.filter(val => isNumber(val))
.map(val => {
// do your stuff
})
I have list of objects as described below:
List<Maths> mObjs = new List<Maths>();
mObjs.Add(new Maths{ Name = "Jack", M1 = 10, M2 = 5, M3 = 0, M4 = 2, M5 =1 });
mObjs.Add(new Maths { Name = "Jill", M1 = 2, M2 = 3, M3 = 4, M4 = 1, M5 = 0 });
mObjs.Add(new Maths { Name = "Michel", M1 = 12, M2 = 15, M3 = 10, M4 = 12, M5 = 11 });
Now I need to calculated the total aggregated value for all three people.
I need to get the below results, probably a new other class
List<Results> mRes = new List<Results>();
public class Results{
public string Name { get; set; }
public int TotalValue { get; set; }
}
mRes.Name = "M1"
mRes.TotalValue = 24;
mRes.Name = "M2"
mRes.TotalValue = 23;
mRes.Name = "M3"
mRes.TotalValue = 14;
mRes.Name = "M4"
mRes.TotalValue = 15;
mRes.Name = "M5"
mRes.TotalValue = 12;
How can I get this data from mObjs using linq query? I know we can do it using for, but want to know if there are any better ways to get this using linq query because that reduces lines of code and I have similar requirements in many other places and dont want to write number of foreach or fors every time.
You can use a pre selection list to list both the name and the field to select
var lookups = new Dictionary<string,Func<Maths,int>> {
{"M1", x => x.M1 },
{"M2", x => x.M2 },
{"M3", x => x.M3 },
{"M4", x => x.M4 },
{"M5", x => x.M5 },
};
Then you can simply do
var mRes = dlookups.Select(x => new Results {
Name= x.Key,
TotalValue = mObjs.Sum(x.Value)
}).ToList();
BEGIN UPDATED*
In response to comments
The lambda expression is just a function from your source class to an int.
For example
class Sub1 {
string M3 {get;set;}
int M4 {get;set;}
}
class Math2 {
string Name {get;set;}
string M1 {get;set;}
string M2 {get;set;}
Sub1 Sub {get;set;}
}
var lookups = new Dictionary<string,Func<Math2,int>> {
{ "M1", x => int.Parse(x.M1) },
{ "M2", x => int.Parse(x.M2) },
{ "M3", x => int.Parse(x.Sub.M3) },
{ "M4", x => int.Parse(x.Sub.M4} }
};
Or if you want to put a little error checking in, you can either use functions or embed the code.
int GetInt(string source) {
if (source == null) return 0;
int result;
return int.TryParse(source, out result) ? result : 0;
}
var lookups = new Dictionary<string,Func<Math2,int>> {
{ "M1", x => {
int result;
return x == null ? 0 : (int.TryParse(x,out result) ? result : 0);
},
{ "M2", x => GetInt(x) },
{ "M3", x => x.Sub == null ? 0 : GetInt(x.Sub.M3) },
{ "M4", x => x.Sub == null ? 0 : x.Sub.M4}
};
END UPDATED
If you want to go further you could use reflection to build the lookups dictionary.
Here is a helper function that will generate the lookups for all Integer properties of a class.
public Dictionary<string,Func<T,int>> GenerateLookups<T>() where T: class {
// This just looks for int properties, you could add your own filter
var properties = typeof(T).GetProperties().Where(pi => pi.PropertyType == typeof(int));
var parameter = Expression.Parameter(typeof(T));
return properties.Select(x => new {
Key = x.Name,
Value = Expression.Lambda<Func<T,int>>(Expression.Property(parameter,x),parameter).Compile()
}).ToDictionary (x => x.Key, x => x.Value);
}
Now you can just do:
var mRes=GenerateLookups<Maths>().Select( x => new Results
{
Name = x.Key,
TotalValue = mObjs.Sum(x.Value)
}).ToList();
Not very smart but efficient and readable:
int m1Total= 0;
int m2Total= 0;
int m3Total= 0;
int m4Total= 0;
int m5Total= 0;
foreach(Maths m in mObjs)
{
m1Total += m.M1;
m2Total += m.M2;
m3Total += m.M3;
m4Total += m.M4;
m5Total += m.M5;
}
List<Results> mRes = new List<Results>
{
new Results{ Name = "M1", TotalValue = m1Total },
new Results{ Name = "M2", TotalValue = m2Total },
new Results{ Name = "M3", TotalValue = m3Total },
new Results{ Name = "M4", TotalValue = m4Total },
new Results{ Name = "M5", TotalValue = m5Total },
};
Result:
Name: "M1" TotalValue: 24
Name: "M2" TotalValue: 23
Name: "M3" TotalValue: 14
Name: "M4" TotalValue: 15
Name: "M5" TotalValue: 12
Edit: since you've explicitly asked for LINQ, if the properties are always these five i don't see why you need to use LINQ at all. If the number can change i would use a different structure.
You could for example use
a single List<Measurement> instead of multiple properties where Measurement is another class that stores the name and the value or you could use
a Dictionary<string, int> for efficient lookup.
You can try out some thing like this :
mRes.Add(new Results() { Name = "M1", TotalValue = mObjs.Sum(x => x.M1) });
To programmatically iterate through all the class properties, you might need to employ reflection.