Keys to Mongoose Object are different from property names - javascript

In order to troubleshoot an error I have been getting, I wrote the following snippet:
var myFunction = function(obj) {
var keys = Object.getOwnPropertyNames(obj);
console.log(obj);
console.log(keys);
}
When running my function within a mongoose query callback, the console logs this:
{_id: 5a8g123vjsdj83nf8afvn48,
username: 'Player1',
adv1: { name: 'a', type: '!' },
adv2: { name: 'a', type: '!' },
adv3: { name: 'a', type: '!' },
__v: 0,
invitations: [ 'PlayTest1', 'PlayTest2' ] }
[ '$__', 'isNew', 'errors', '_doc', '$init' ]
Now as far as I understand it, the last line in the console (separated for reading convenience) should read:
[ '_id', 'username', 'adv1', 'adv2', 'adv3', '__v', 'invitations ]
My question is why does the keys obj I create in myFunction not contain the properties names shown when I log the actual object?

Mongoose document fields are stored in obj._doc
console.log displays the document fields due to an .inspect function attached to the document object.
Use node --inspect if you want to debug something.

Related

Issue encoding array of addresses for Web3

I'm trying to call the Uniswap Router contract like:
// dummy data
Function: swapExactETHForTokens(uint256 amountOutMin, address[] path, address to, uint256 deadline)
MethodID: 0x7ff36ab5
[0]: 000000000000000000000000000000000000000000000000000000dd0f593444
[1]: 0000000000000000000000000000000000000000000000000000000000000080
[2]: 00000000000000000000000047c0a182235478ca13d248d049eaa28d4ff7520f
[3]: 000000000000000000000000000000000000000000000000000000005f9a1e44
[4]: 0000000000000000000000000000000000000000000000000000000000000002
[5]: 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
[6]: 0000000000000000000000001a969239e12f07281f8876d11afcee081d872adf
In my TypeScript code I have:
const abi = web3.eth.abi.encodeFunctionCall(
{
type: 'function',
name: 'swapExactTokensForTokens',
inputs: [
{
name: 'amountIn',
type: 'uint256',
},
{
name: 'amountOutMin',
type: 'uint256',
},
{
name: 'path',
type: 'address[]',
},
{
name: 'to',
type: 'address',
},
{
name: 'deadline',
type: 'uint256',
},
],
},
[
fromWei,
toWei,
// the problem
web3.eth.abi.encodeParameter('address[]', [
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
'0x20fe562d797a42dcb3399062ae9546cd06f63280',
]),
getAddress(),
deadline,
]
)
From the error, it looks like encodeFunctionCall wasn't expecting an encoded array (at least, the way I did it). So how would I pass an array of addresses?
(node:21008) UnhandledPromiseRejectionWarning:
Error: expected array value
(argument="path", value="0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000020fe562d797a42dcb3399062ae9546cd06f63280", code=INVALID_ARGUMENT, version=abi/5.0.0-beta.153)
Full source: https://github.com/ZaneH/tradr/blob/56d670aeec09ed0e8c3e359c48ae8b36fdea7c30/packages/backend/src/modules/Trades.ts#L37
I believe that web3.eth.abi.encodeFunctionCall expects JSON type parameters, e.g. address[] is represented as an simple array of strings ["0x...", "0x..."]
// the problem
web3.eth.abi.encodeParameter('address[]', [
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
'0x20fe562d797a42dcb3399062ae9546cd06f63280',
]),
Instead of Please try:
[
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"0x20fe562d797a42dcb3399062ae9546cd06f63280"
],

Convert user input string to an object to be accessed by function

I have data in the format (input):
doSomething({
type: 'type',
Unit: 'unit',
attributes: [
{
attribute: 'attribute',
value: form.first_name
},
{
attribute: 'attribute2',
value: form.family_name
}
],
groups: [
{
smth: 'string1',
smth2: 'string2',
start: timeStart.substring(0, 9)
}
]
})
I managed to take out the doSomething part with the parenthesis as to load the function from the corresponding module with
expression.split('({',1)[0]
However using the loaded function with the rest, obtained with:
expression.split(temp+'(')[1].trim().replace(/\n+/g, '').slice(0, -1)
does not work because it should be an object and not a string. Hardcoding the data in does work as it is automatically read as an object.
My question is if there is any way of converting the string that I get from the user and convert it to an object. I have tried to convert it to a json object with JSON.parse but I get an unexpected character t at position 3. Also I have tried new Object(myString) but that did not work either.
What I would like is to have the body of the provided function as an object as if I would hard code it, so that the function can evaluate the different fields properly.
Is there any way to easily achieve that?
EDIT: the "output" would be:
{
type: 'type',
Unit: 'unit',
attributes: [
{
attribute: 'attribute',
value: form.first_name
},
{
attribute: 'attribute2',
value: form.family_name
}
],
groups: [
{
smth: 'string1',
smth2: 'string2',
start: timeStart.substring(0, 9)
}
]
}
as an object. This is the critical part because I have this already but as a string. However the function that uses this, is expecting an object. Like previously mentioned, hard coding this would work, as it is read as an object, but I am getting the input mentioned above as a string from the user.
Aside: I know eval is evil. The user could do by this certain injections. This is only one possibility to do this there are certain other ways.
I just added before "output =", cut from the input-string the "doSomething(" and the last ")". By this I have a normal command-line which I could execute by eval.
I highly not recommend to use eval this way; especially you don't
know what the user will do, so you don't know what could all happen
with your code and data.
let form = {first_name: 'Mickey', family_name: 'Mouse'};
let timeStart = (new Date()).toString();
let input = `doSomething({
type: 'type',
Unit: 'unit',
attributes: [
{
attribute: 'attribute',
value: form.first_name
},
{
attribute: 'attribute2',
value: form.family_name
}
],
groups: [
{
smth: 'string1',
smth2: 'string2',
start: timeStart.substring(0, 9)
}
]
})`;
let pos= "doSomething(".length;
input = 'output = ' + input.substr(pos, input.length-pos-1);
eval(input);
console.log(output);

How can I remove object from array, with Lodash?

I'm trying to remove an object from an array using Lodash.
In server.js (using NodeJS):
var lodash = require('lodash')();
var rooms = [
{ channel: 'room-a', name: 'test' },
{ channel: 'room-b', name: 'test' }
]
I tried with two commands and it did not work:
var result = lodash.find(rooms, {channel: 'room-a', name:'test'});
var result = lodash.pull(rooms, lodash.find(rooms, {channel: 'room-a', name:'test'}));
Here's the output of console.log(result):
LodashWrapper {
__wrapped__: undefined,
__actions__: [ { func: [Function], args: [Object], thisArg: [Object] } ],
__chain__: false,
__index__: 0,
__values__: undefined }
Can someone help me? Thank you!
_.remove() is a good option.
var rooms = [
{ channel: 'room-a', name: 'test' },
{ channel: 'room-b', name: 'test' }
];
_.remove(rooms, {channel: 'room-b'});
console.log(rooms); //[{"channel": "room-a", "name": "test"}]
<script src="https://cdn.jsdelivr.net/lodash/4.14.2/lodash.min.js"></script>
I'd go for reject() in this scenario. Less code:
var result = _.reject(rooms, { channel: 'room-a', name: 'test' });
require('lodash')()
Calling the lodash function (by ()) creates a LoDash object that wraps undefined.
That's not what you want; you want the lodash function itself, which contains static methods.
Remove that.

Knockout Mapping toJSON - cannot ignore nested child

Assume I have a viewModel like below.
var data = {
a: { a1: "a1", a2: "a2" },
b: "b"
};
I would like to ignore a.a1 and b. So my expected JSON is
{"a":{a2:"a2"}}
However, on doing this
var result = ko.mapping.toJSON(data, { ignore: ["a.a1", "b"] })
I am getting result=
{"a":{"a1":"a1","a2":"a2"}}
Knockout mapping is not ignoring a.a1. Is this a bug in the plugin? It correctly ignored 'b' but why not 'a.a1'?
The names found in the ignore array should be the name of the property, regardless of what level it is in the object. You have to use:
{ ignore: [ "a1", "b" ] }
I had similar ignore list where some "ids" have to be suppressed and others left as is. I wanted to expand on the answer so that people using fromJS can see specific ignores do work
var data1 = {
invoice: { id: 'a1', name: 'a2', type: 'a3'},
shipping: "b1",
id:"c1"
};
var resultvm = ko.mapping.fromJS(data1, {'ignore':["invoice.id",
"ship"]}); ko.applyBindings(resultvm);
Will give you an output as below. Notice that only the id for invoice has been ignored.
{"invoice":{"name":"a2","type":"a3"},"id":"c1"}
But toJSON gives
Code:
var result = ko.mapping.toJSON(data1, { ignore: ["invoice.id",
"shipping"] });
Result:
{"invoice":{"id":"a1","name":"a2","type":"a3"},"id":"c1"}
Here is my jsFiddle: https://jsfiddle.net/3b17r0ys/8/

Nested Javascript Object : recursion and "complex" transformation (with lodash)

I apologize in advance for the complex example here; I tried to trim it down as much as I could to illustrate what I try to achieve
I have a complex structure that I need to traverse and transform based on some conditions; Here's an (short) example of the structure that should cover most scenarios:
{ PROP1: {
metadata: Object, // somewhere deeper in metadata I have a `value` key
parent: { $ref: String },
employee: {
parent: { $ref: String },
id: String,
metadata: Object,
products: {
metadata: Object,
model: { $ref: String },
type: 'array',
value: ["String", "String" , "String"] ,
[...]
},
model: {
id: String,
email: {
metadata: Object,
value: 'a#b.com',
type: 'string',
validity: Object,
[...]
},
name: {
firstName: {
metadata: Object,
value: 'John',
type: String,
validity: Object,
[...]
},
lastName: {
metadata: Object,
value: 'Smith',
type: String,
validity: Object,
[...]
},
}
},
operations: {
id: String,
items: [
{ action: {value: "UPDATE", model: {$ref: String }, [...] },
{...}
],
metadata: Object,
[...]
}
}
},
PROP2: {
// similar as PROP1
},
[... and so on ...]
}
I basically need to clean that up before sending it to the backend;
Whenever a value contains $ref, I don't want the key/val pair (e.g.: PROP1.parent is of no use and can be omitted)
whenever a value contains value, I need to omit everything else and move the value of value as the value of key (e.g.: PROP1.employee.products should equal ['String', 'String', 'String'])
keys like id, metadata, validity (etc) can be completely omitted regardless of its content
So the end result should be along those lines:
{ PROP1: {
employee: {
products: ['item','item','item'],
model: {
email: 'a#b.com',
name: { firstName: 'John', lastName: 'Smith'},
},
operations: [
{action: 'UPDATE'}
]
}
},
PROP2: { ... }
}
I tried lots of different approaches using different lodash methods but couldn't wrap my head around this...
Any help will be greatly appreciated
Thanks
In pseudo code, try something like this. Implement the specifics and post more info when you run into trouble.
var ignoreKeyArray = ["id", ...] // keys to ignore and remove
var newJSON = "{}";
for (property in JSON) {
var newProp = parseJSON(key, property);
insert newProp in newJSON object
}
var parseJSON = function (key, jsonBlob) {
if (key in ignoreKeyArray || jsonBlob contains value "$ref")
return "";
var jsonOut = key + ": {}";
for (child in jsonBlob) {
add parseJSON(child) to jsonOut;
}
return jsonOut;
}
If you have any questions, comment so I can extend the answer and clarify.

Categories

Resources