Cheerio Map Strange Behaviour - node.js

I'm using map with a list of Cheerio results to return an attribute value. What I want is a variable that contains a list of attribute values (in this case ID's), but instead I'm getting the ID's and extra data.
The following code prints a list of ID's:
let ids = $('[data-profileid]').map(function() {
console.log($(this).attr('data-profileid'))
})
Result:
1012938412
493240324
123948532
423948234
...
But, the following code returns the IDs but in a different format:
let ids = $('[data-profileid]').map(function() {
return $(this).attr('data-profileid')
})
console.log(ids)
Results:
...
'69': '234234234,
'70': '9328402397432',
'71': '1324235234',
options:
{ withDomLvl1: true,
normalizeWhitespace: false,
xmlMode: false,
decodeEntities: true },
_root:
{ '0':
{ type: 'root',
name: 'root',
attribs: {},
...
What is all this extra data? It certainly isn't required. I'd rather just have an ordinary array.

According to http://api.jquery.com/map/:
As the return value is a jQuery object, which contains an array, it's
very common to call .get() on the result to work with a basic array.
So it looks like this should work:
let ids = $('[data-profileid]').map(function() {
return $(this).attr('data-profileid')
}).get()

What is all this extra data? It certainly isn't required. I'd rather just have an ordinary array.
Cheerio has a fluent API, meaning most of its functions return an object on which additional functions can be chained. If map just returned an "ordinary array" then you wouldn't be able to call additional Cheerio functions on the result. There aren't a lot of ways you can chain additional function calls onto the result of your map call, which returns an array of strings, but Cheerio's developers (taking a cue from jQuery's developers) chose to keep a consistent API rather than pepper it with special cases.
If you want an ordinary array, though, Cheerio gives you a handy toArray function:
let ids = $('[data-profileid]').map(function() {
return $(this).attr('data-profileid')
});
console.log(ids.toArray());
// => [ '1012938412', '493240324', '123948532', '423948234' ]

Related

Array of Structs returning unusual data in Solidity

I am creating one Voting Smart Contract people can organize one election and voters can vote for their candidate. I have created one function which will return the statistics of ongoing or past elections
///#dev making statistics for all ballot/election
///#return results with all the information of all
function getStatisticsOfAllVote()
public
view
returns (SingleElectionStatistics[] memory )
{
SingleElectionStatistics[] memory results = new SingleElectionStatistics[](BallotArray.length);
for (uint256 i = 0; i < BallotArray.length; i++) {
SingleElectionStatistics memory temp = SingleElectionStatistics(
BallotArray[i]._getName(), //CEO election
BallotArray[i]._getDescription(),//Employees will choose their CEO
BallotArray[i]._getTotalVoteCounted(),//BigNumber { value: "1" }
BallotArray[i]._isVotingEnded(),//true
BallotArray[i]._getWinningProposalName()//John
);
results[i] = temp;
}
return results;
}
Sample returns are added as comments after the function call.
I suppose to get one array of objects. Buts it gave me data unusual format with extra data. Here are the returns data:
[
[
'CEO election',
'Employees will choose their CEO',
BigNumber { value: "1" },
true,
'John',
name: 'CEO election',
description: 'Employees will choose their CEO',
voteCounted: BigNumber { value: "1" },
voteEnded: true,
winningProposalName: 'John'
]
]
Which should return only
[
{
name: 'CEO election',
description: 'Employees will choose their CEO',
voteCounted: BigNumber { value: "1" },
voteEnded: true,
winningProposalName: 'John'
}
]
I may need help from the community. Thanks in advance
I tried with unit tests and also by changing the approaches but it doesn't help at all.
The raw data returned from a node (using eth_call RPC method) is an ABI-encoded byte array, containing each item just once.
The duplication that you see is caused by an offchain framework. Based on other context, I'm assuming that you're using ethers.js.
Their docs page says:
A Result is an array, so each value can be accessed as a positional argument.
Additionally, if values are named, the identical object as its positional value can be accessed by its name.
In your case, the call returns an array of Result types. Since the Solidity return variables are named (your ethers.js instance knows this from the ABI JSON generated during Solidity compilation), each Result type contains both number-indexed and named items.

Avoid duplicate code when api might return array or single object

I'm working with an API to consult car debits. If the car has more than one debit, the API returns an array of debits. If it has only one, it returns a single debit object (not an array with one element).
The problem is that I have to duplicate all the deserialization of this response checking whether the attribute is an array or a single object.
const debits = []
if (car.debits.length > 0) {
car.debits.forEach((debit: any) => {
debits.push({
id: uuidv1(),
description: debit.description,
label: debit.label,
amount: parseInt(debit.amount, 10)
})
})
} else {
debits.push({
id: uuidv1(),
description: debit.description,
label: debit.label,
amount: parseInt(debit.amount, 10)
})
}
Is there any way to simplify this? I showed just a small example but the object is much larger and there are many other attributes that I have to do the same.
If you have control over the API, you should probably have it return an array with a single element. If not, at the start of the function just force it into an array.
car.debits = car.debits.length ? car.debits : [car.debits]
If car.debits.length is undefined, which means it is not an array, you create an array and put the object inside it

Promise problems - .then() not getting called?

I have two Sequelize queries that I need to run in a specific order, the first creates a model and the second creates multiple models from an array. It works in that it inserts obj1 first and then the array of transactions, however the final 'then()' doesn't seem to get called... What am I doing wrong?
var fn = function addTransaction(transaction) {
return new Promise(function() {
return Transaction.create(transaction, { fields: [ 'Id', 'Name' ]});
}
};
var transactions = [...]; //array of transaction objects
Model1.create(obj1, { fields: [ 'Id', 'Name' ,'Description' ]}).then(function() {
var actions = transactions.map(fn);
var results = Promise.all(actions).then(function(data) {
return Promise.all(data.map(fn));
});
results.then(function() {
console.log("Done?"); //not getting called
});
}).catch(function(err) {
console.log(err);
});
addTransaction() is never going to resolve the promise it creates. You create an outer promise and return it, but never resolve it. You would at least have to declare a resolve argument to that callback and then call resolve() somewhere.
But, if Transaction.create() already returns a promise, then you can change addTransaction() to this:
var fn = function addTransaction(transaction) {
return Transaction.create(transaction, { fields: [ 'Id', 'Name' ]});
};
It's also unclear why you're using the same fn to process both the transactions array and the data array that results from each of those transactions. That seems a little odd from a logic point of view. Are you sure that's the correct way to write your code? For us to help you with the design of that part of the code, you'd have to explain more about what you're trying to accomplish by calling fn on both the first array of transactions and then again on the results from that.
I'm not sure if this will solve your problem exactly, but I think you are missing a return:
...
return results.then(function() {
console.log("Done?"); //not getting called
});
...
Without it the Model1.create promise would resolve before the results finish.

How to return data as JSON in Sequelize

When I make a Sequelize query it returns to me an object (or array) which I'm guessing is a Sequelize model (or array of Models (a collection type??)) but it's not documented anywhere, so I'm just guessing. I would always like the results to be JSON. Is there anything I can pass in the query to force this? I would prefer not to hand massage every result I get back to be JSON if possible.
The documentation show this to return a string:
console.log(JSON.stringify(users))
So there's some built in serialization. Right now I'm doing this, using the undocumented toJSON() method:
query().then(function(result) {
if(result.length) {
return result.toJSON();
} else {
return result.map(function(item) {
return item.toJSON();
});
}
});
which is cumbersome.
You can use raw: true in the Query however this does not always behave as you might expect, especially with associations.
query({
// ...
raw: true
}).then(function(result) {
// Result is JSON!
});
However in the case where you're using associations you may get something like this:
{
foo: true,
"associated.bar": true
}
Instead of what you might expect:
{
foo: true,
associated: {
bar: true
}
}
When you retrieve the model from the database, you can call the .get({ plain: true}) on the result and it will handle the conversion for you. You can assign the value of the function call to a variable. For example
..).then(function(user){
var _user = user.get({ plain: true});
console.log(_user); //Should be valid json object
});
Hope this helps.
If you're doing a query with which has multiple results you should expect an array to be returned. You should find that each element in the result array is a JSON object.
You should be able to access a given field like this: result[0].myfieldname

Node dbus-native

The documentation for node module dbus-native is weak, and the none of the many examples apply to my use case, which seems like it ought to be simple.
My problem is trying to make a simple method call into the "connman" network manager. I can make a request (invoking GetProperties method) just fine, and get a complex structure back and generally figure out how to get data out of it. But when I try to send something back with SetProperty, I just can't figure out how to get my data into the form it wants.
There's not really any documentation about how to translate to and from DBus's type system and node's. I gathered from source that variant types are arrays with signature and value, e.g., [ 's', 'string ], but no matter how I try to massage the data I'm trying to send to SetProperty, I get an "Invalid struct data" error.
The SetProperty method I'm trying to call has an argument signature "sv"--name, value. The particular property I'm trying to set has a value with is itself (as far as I can determine--there's precious little documentation on that side as well) an "a{sv}". that is,property is a hash of property names and values. My initial attempt as calling this function was:
var sysbus = require('dbus-native').systemBus();
sysbus.invoke({
path: '/net/connman/service/ethernet_1cba8cfa0e57_cable',
destination: 'net.connman',
'interface': 'net.connman.Service',
member: 'SetPropertry',
signature: 'sv',
body: [
'IPv4.Configuration', [
'a{sv}',
{ 'Method': [ 's', 'dhcp' ] }
]
],
type: dbus.messageType.methodCall
}, function (err, res) {
// etc...
});
This gives me the "Invalid struct data" error. I've tried man other ways to wrap the data in arrays, hashes, etc., and just can't seem to find the right answer. I'm trying to emulate this working Python code:
import dbus
bus = dbus.SystemBus()
service = dbus.Interface(bus.get_object("net.connman",
"/net/connman/service/ethernet_1cba8cfa0e57_cable",
"net.connman.Service");
conf = { "Method": make_string_variant("dhcp") }
service.SetProperty("IPv4.Configuration", conf);
Any ideas?
I recently started using dbus-native for connman and also had problems with all the nested arrays. I'll be improving the marshaling/unmarshaling in the fork of the project at https://github.com/Digisoft-tv/node-dbus - you might want to have a look. Hopefully my changes will be accepted up-stream.
Anyway, to get it working now, the format of the parameters you need to pass in is as below:
mgr.CreateSession([ [ 'key', [ 's', 'value' ] ], [ 'key2', [ 'i', 2 ] ] ], '/object/path', function(error, response) {
if (error) {
return console.error('SetProperty error:', error);
}
console.info('SetProperty response', response);
});
The outermost array is the "object" (ARRAY of dict entries).
Each nested array holds a key-value pair (DICT ENTRY). key at index 0 and value at index 1.
The values is a variant, which is always encoded as an array, with "signature" at index 0 and the actual value at index 1.
Hope it helps.
The correct way to encode 'a{sv}' is [ ['string', ['signature', 'value']], ... ]
Arrays ( a ) are encoded as normal JS arrays as well as structs / hashes ( () / {} ). I'll probably add shortcut to allow JS object to be used in place of a{sv} but at the moment input has to be quite verbose.
var sysbus = require('dbus-native').systemBus();
sysbus.invoke({
path: '/net/connman/service/ethernet_1cba8cfa0e57_cable',
destination: 'net.connman',
'interface': 'net.connman.Service',
member: 'SetPropertry',
signature: 'sv',
body: [
'IPv4.Configuration', [
'a{sv}',
[ // a
[ // {
'Method', [ 's', 'dhcp' ] // sv
]
]
]
],
type: dbus.messageType.methodCall
}, function (err, res) {
// etc...
});

Resources