As i understand it, a typical JSON array could be something like this:
{ "measurements":[ {"order":1, "time":"23:55:12.234", "value":3.4543235},
{"order":2, "time":"23:55:14.118", "value":3.9785742},
{"order":3, "time":"23:55:15.942", "value":3.6892639}]
}
But since i have several hundred measurements, i would like to remove the repeating keys like this:
1:
{ "measurements":[ 1, "23:55:12.234", 3.4543235,
2, "23:55:14.118", 3.9785742,
3, "23:55:15.942", 3.6892639]
}
And if i do it like that i also would like to keep the keys for reference - to allow other sequences and maybe even additional information in the array, i could imagine something like this:
2:
{ "measurements":{"assembly":"order,time,value",
"values": [ 1, "23:55:12.234", 3.4543235,
2, "23:55:14.118", 3.9785742,
3, "23:55:15.942", 3.6892639]
}
}
Are 1 and or 2 correct JSON and if not is there another way?
And even if that is allowed - is there a more sensible / common way of doing this?
Related
I have an array (obj_values) of objects
[
#<User id: 1, name: "Kostas">,
#<User id: 2, name: "Moufa">,
...
]
And I want to convert this into an Array with only the values from above objects, so it will look like:
[
1, Kostas
2, Moufa
]
I can do it like this:
obj_table = []
obj_values.each do |ext|
ext.each do |obj|
obj_table.push([obj.id, obj.name].join(","))
end
end
However with this approach I need to explicitly specify what attributes I want to push (obj.id and obj.name), is there a way to push whole data from object without the need to specify each attribute separately?
Use .attributes to get a hash of all the attributes on the model. Call .values on it to get just the values without the keys.
Using your code:
obj_table = []
obj_values.each do |ext|
ext.each do |obj|
obj_table.push(obj.attributes.values.join(","))
end
end
Though there are better ways. I suggest you look into .map and .flat_map.
obj_table = obj_values.flat_map do |ext|
ext.map do |obj|
obj.attributes.values.join(",")
end
end
Yes, you can push data without specifying it separately. to push data you have to use an object.attributes.values
an object will be your active record object.
attributes will return a hash with all attributes of that object.
so you can change your loops into a single statement like:
obj_values.flat_map {|object_value| object_value.attributes.values }
I'm building a program in React that is basically a questionnaire / survey, so far I've got users answering a bunch of questions then what I am trying to do is take the answers, filter them a bit then return an object to store using Redux. Each question is an object it self that holds different factors than I am trying to measure. Although, I'm getting stuck with actually trying to create the structure of the final object.
I want the object to look something like this:
Results {
Question1 {
factor1: 1,
factor2: 2,
factor3: 3
},
Question2 {
factor1: 3,
factor2: 6
},
and so on...
}
But when I try create create it by saying for example
let results = {
Question1 {
factor1: 0
}
}
React doesn't like it. As soon as I try nest another object inside it returns an error.. Am I missing something simple or do I need to try another approach, any help would be appreciated
It should be like this, property and value needs to be separated by a :
let results = {
Question1 :{
factor1: 0
}
}
let Results= {
Question1: {
factor1: 1,
factor2: 2,
factor3: 3
},
Question2: {
factor1: 3,
factor2: 6
},
and so on...
}
Is it possible to wildcard the key in a query? For instance, given the following record, I'd like to do a .find({'a.*': 4})
This was discussed here https://jira.mongodb.org/browse/SERVER-267 but it looks like it's not been resolved.
{
'a': {
'b': [1, 2],
'c': [3, 4]
}
}
As asked, this is not possible. The server issue you linked to is still under "issues we're not sure of".
MongoDB has some intelligence surrounding the use of arrays, and I think that's part of the complexity surrounding such a feature.
Take the following query db.foo.find({ 'a.b' : 4 } ). This query will match the following documents.
{ a: { b: 4 } }
{ a: [ { b: 4 } ] }
So what does "wildcard" do here? db.foo.find( { a.* : 4 } ) Does it match the first document? What about the second?
Moreover, what does this mean semantically? As you've described, the query is effectively "find documents where any field in that document has a value of 4". That's a little unusual.
Is there a specific semantic that you're trying to achieve? Maybe a change in the document structure will get you the query you want.
I've came across this question because I faced the same issue. The accepted answer provider here does explains why this is not supported but not really solves the issue itself.
I've ended up with a solution that makes the wildcard usage purposed here redundant and share here just in case someone will find this post some day
Why I wanted to use wildcards in my MongoDB queries?
In my case, I needed this "feature" in order to be able to find a match inside a dictionary (just as the question's code demonstrates).
What's the alternatives?
Use a reversed map (very similar to how DNS works) and simply use it. So, in our case we can use something similar to this:
{
"a": {
"map": {
"b": [1, 2, 3],
"c": [3, 4]
},
"reverse-map": {
"1": [ "b" ],
"2": [ "b" ],
"3": [ "b", "c" ],
"4": [ "c" ]
}
}
}
I know, it takes more memory and insert / update operations should validate this set is always symmetric and yet - it solves the problem. Now, instead of making an imaginary query like
db.foo.find( { a.map.* : 4 } )
I can make an actual query
db.foo.find( { a.reverse-map.4 : {$exists: true} } )
Which will return all items that have a specific value (in our example 4)
I know - this approach takes more memory and you need to manage indexes properly if you want to gain good performance (read the docs) and still - it's good for my use-case. Hope this helps someone else someday as well
Starting from MongoDB v3.4+, you can use $objectToArray to convert a into an array of k-v tuples for querying.
db.collection.aggregate([
{
"$addFields": {
"a": {
"$objectToArray": "$a"
}
}
},
{
$match: {
"a.v": 4
}
},
{
"$addFields": {
// cosmetics to revert back to original structure
"a": {
"$arrayToObject": "$a"
}
}
}
])
Here is the Mongo playground for your reference.
I need length of books that added after item, here is my controller:
$scope.items = [
{"added_days_ago": 5, "books": [{"id":1, "added_days_ago": 6}, {"id":2, "added_days_ago": 3}, {"id":3, "added_days_ago": 4}]},
{"added_days_ago": 2, "books": [{"id":4, "added_days_ago": 3}]}
]
In controller i need to define filteredBooksLength (i using ng-table) and then show like:
{{filteredBooksLength}}
Something like:
filteredBooksLength = (books where book.added_days_ago > item.added_days_ago).length
JSFiddle
$scope.filteredBooksLength = function (item) {
return item.books.filter(function (book) {
return book.added_days_ago > item.added_days_ago
}).length
Should cover your needs - summon it within the ng-repeat block, and pass in the current item - filteredBooksLength(item)
jsFiddle
How it works
We're giving the scope access to a function that accepts an item object as an argument. We take the books array on that object and pass it though a filter function, which returns only those books where book.added_days_ago is greater than item.added_days_ago as an array. We then measure the length of that array and return it.
Note on style
From what you've written - and the way you've phrased your question - it looks like you've done some Python. It's accepted practice in JS to use camelCase rather than snake_case - you may wish to change that for the the keys in your items object. In addition, the "" around those keys are superfluous.
Keep at it!
I've 18 documents in my collection movie. For each movie for example:
{
title: "Test Movie 2",
date: [20130808, 20130606],
score: [ {"pete": 1, "mary": 1, "simon": 1, "pat": 2, "mike": 0},
{"pete": 5, "mary": 5, "simon": 5, "pat": 0, "mike": 5}]
}
Now, I want to show the date and sum of the second document in the array 'score' on the client, like:
<div class="details">
Test Movie 2: 20 points 20130606
</div>
Have somebody an idea how I can do that?
You could use a transform, it might be better to define each document with a name and explicitly defining the points as name/value pair instead of points being the value for each persons name.
But this should work:
Movies.find({}, {transform: function(doc) {
var total_points = 0;
var people = doc.score[1]; //Second in array
for(point in people) {
total_points += people[point]
}
doc.points = total_points;
return doc;
}});
This should give you:
{
title: "Test Movie 2",
points: 20,
date: [20130808, 20130606],
score: [ {"pete": 1, "mary": 1, "simon": 1, "pat": 2, "mike": 0},
{"pete": 5, "mary": 5, "simon": 5, "pat": 0, "mike": 5}]
}
Mongo can likely do this outright, but you're not going to be able to do this directly by querying a collection due to limitation of the Mongo livedata package as of 0.6.5. Aggregation framework is also off-limits, and they seem to have pulled the 'hidden' method that allowed direct access to Mongo.
So your options are:
Use transform as per Akshat's answer - the best of both worlds
Aggregate manually in the client in a template helper. I recommend using _.js which comes 'free' with Meteor (again this might change, but you could always pull the library in manually later).
var sum = _.reduce(score[1], function(memo, num){ return memo + num; }, 0);
(I didn't test the above, but it should send you on the right track).
Aggregate upstream, during the insert/update/deletes, likely by observing changes on the collection and 'feeding in' the sum() of the elements you are inserting in either the same collection or an aggregate one.
Which method you use depends on where performance matters most to you, usually doing aggregates before you insert tend to avoid issues later on, and lightens load.
Good luck and let us know how you get on.