What i want is to slice an array inside an array and return ONLY the queried array element. But i get instead every element from the first array.
I have this example input
[
{
"name": "admin",
"datasets": [
{
"name": "test",
"datapoints": [
1,
6,
4,
3,
8,
5,
3
],
"_id": {
"$oid": "619288f16733758444a28728"
}
},
{
"name": "more datasets",
"datapoints": [
1,
2,
3,
4,
5,
6,
7,
8
],
"_id": {
"$oid": "619289086733758444a2872a"
}
}
]
}
]
I have tried it with
db.collection.find({
name: "admin",
"datasets.name": "test"
},
{
"datasets.datapoints": {
$slice: [
0,
3
]
}
})
The problem here is that i get every item of datasets
[
{
"_id": ObjectId("5a934e000102030405000000"),
"datasets": [
{
"_id": ObjectId("619288f16733758444a28728"),
"datapoints": [
1,
6,
4
],
"name": "test"
},
{
"_id": ObjectId("619289086733758444a2872a"),
"datapoints": [
1,
2,
3
],
"name": "more datasets"
}
],
"name": "admin"
}
]
But i just need the 1
Result should just be:
{ datapoints: [1, 6, 4, 3] }
Query
map the datasets
if name :"test" take the slice of the first 4 members
else null
filter to remove the null, so it remains only the "test" and with first 4 members
result is array of arrays, because we might have many "test" members, if you are sure it will be always max 1 take the first member with $first or $arrayElemAt
*on find criteria if you have too many admins, you can add also "datasets.name": "test" if you have index on "datasets.name" it can help.
PlayMongo
aggregate(
[{"$match": {"name": {"$eq": "admin"}}},
{"$set":
{"datasets":
{"$map":
{"input": "$datasets",
"in":
{"$cond":
[{"$eq": ["$$this.name", "test"]},
{"datapoints": {"$slice": ["$$this.datapoints", 0, 4]},
"_id": "$$this._id",
"name": "$$this.name"},
null]}}}}},
{"$project":
{"_id": 0,
"datasets":
{"$filter":
{"input": "$datasets", "cond": {"$ne": ["$$this", null]}}}}}])
Related
This question already has answers here:
JS (ES6): Filter array based on nested array attributes
(7 answers)
Filtering array of objects with arrays based on nested value
(8 answers)
Closed 10 days ago.
I need to return the object which hobbies is reading, below is my sample scenario
[
{
"id": 1,
"name": "john",
"hobbies": [
{
"id": 1,
"name": "playing"
}
]
},
{
"id": 3,
"name": "peter",
"hobbies": [
{
"id": 3,
"name": "reading"
}
]
}
]
// Expected Output:
[
{
"id": 3,
"name": "peter",
"hobbies": [
{
"id": 3,
"name": "reading"
}
]
}
]
I use filter() and find() method however i couldn't loop it inside hobbies array
Use Array#filter in conjunction with Array#some.
let arr = [ { "id": 1, "name": "john", "hobbies": [ { "id": 1, "name": "playing" } ] }, { "id": 2, "name": "mary", "hobbies": [ { "id": 2, "name": "cleaning" } ] }, { "id": 3, "name": "peter", "hobbies": [ { "id": 3, "name": "reading" } ] } ];
let res = arr.filter(x => x.hobbies.some(h => h.name === 'reading'));
console.log(res);
Use Array.prototype.filter method to filter the outer array and Array.prototype.some method to check if an object in the inner hobbies array has a name property of "reading"
let people = [ { "id": 1, "name": "john", "hobbies": [ { "id": 1, "name": "playing" } ] }, { "id": 2, "name": "mary", "hobbies": [ { "id": 2, "name": "cleaning" } ] }, { "id": 3, "name": "peter", "hobbies": [ { "id": 3, "name": "reading" } ] } ];
let readingPerson = people.filter(person =>
person.hobbies.some(hobby => hobby.name === "reading")
)[0];
console.log(readingPerson);
This question already has answers here:
How to sort an array based on the length of each element?
(12 answers)
Closed 1 year ago.
I have an array like below, and I need to sort the array by the string length of name field.
for an example,
[
{
"_id": 10,
"name": "AAAAAA"
},
{
"_id": 11,
"name": "AA"
},
{
"_id": 12,
"name": "AAAA"
},
{
"_id": 13,
"name": "A"
},
{
"_id": 14,
"name": "AAAAAAAA"
}
]
I need the array like this,
[
{
"_id": 13,
"name": "A"
},
{
"_id": 11,
"name": "AA"
},
{
"_id": 12,
"name": "AAAA"
},
{
"_id": 10,
"name": "AAAAAA"
},
{
"_id": 14,
"name": "AAAAAAAA"
}
]
can any one help me out with this. Thanks.
This can be accomplished with the _.orderBy method:
_.orderBy(data, [({ name }) => name.length, 'name'], ['desc']);
Here is a break-down:
I threw some "B"s into the mix to show the secondary sorting (after length is compared). Sorting the length alone is not unique enough.
const data = [
{ "_id": 1, "name": "AAAAAA" },
{ "_id": 2, "name": "AA" },
{ "_id": 3, "name": "AAAA" },
{ "_id": 4, "name": "A" },
{ "_id": 5, "name": "AAAAAAAA" },
{ "_id": 6, "name": "BBBBBB" },
{ "_id": 7, "name": "BB" },
{ "_id": 8, "name": "BBBB" },
{ "_id": 9, "name": "B" },
{ "_id": 10, "name": "BBBBBBBB" }
];
const sorted = _.orderBy(
data, // Data to be sorted
[
({ name: { length } }) => length, // First, sort by length
'name' // Them sort lexicographically
], [
'desc', // Length (descending)
'asc' // This is implied, and could be removed
]
);
console.log(sorted);
.as-console-wrapper { top: 0; max-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
I have this json:
[
{
"question": "1.1",
"level": 1,
"id": 4,
"answers": [
{
"text_answer": "NO",
"questions": [
{
"question": "1.1.1",
"level": 2,
"id": 3,
"answers": []
}
]
},
{
"text_answer": null,
"questions": [
{
"question": "1.1.2",
"level": 2,
"id": 2,
"answers": [
{
"text_answer": "SI",
"questions": [
{
"question": "1.1.2.1",
"level": 3,
"id": 1,
"answers": []
}
]
}
]
}
]
}
]
}
]
this json is dynamic and can have n amount of levels under the fields answers and questions that is why it is necessary to go through them with a recursive function
I want to get this output:
[
{
"question": "1.1",
"level": 1,
"id": 4,
"children": [
{
"question": "1.1.1",
"text_answer": "NO",
"level": 2,
"id": 3,
"children": []
},
{
"question": "1.1.2",
"text_answer": null,
"level": 2,
"id": 2,
"children": [
{
"question": "1.1.2.1",
"text_answer": "SI",
"level": 3,
"id": 1,
"children": []
}
]
}
]
}
]
the fields answers and questions no longer exist, they are renamed children and the content of both is unified.
I think this recursive function should do a double foreach, but I don't know how to do it.
function format(d) {
if (d.respuestas) {
d.respuestas.forEach((d) => {
format;
});
}
}
format(data);
var data= [
{
"question": "1.1",
"level": 1,
"id": 4,
"answers": [
{
"text_answer": "NO",
"questions": [
{
"question": "1.1.1",
"level": 2,
"id": 3,
"answers": []
}
]
},
{
"text_answer": null,
"questions": [
{
"question": "1.1.2",
"level": 2,
"id": 2,
"answers": [
{
"text_answer": "SI",
"questions": [
{
"question": "1.1.2.1",
"level": 3,
"id": 1,
"answers": []
}
]
}
]
}
]
}
]
}
]
/* [
{
"question": "1.1",
"level": 1,
"id": 4,
"children": [
{
"question": "1.1.1",
"text_answer": "NO",
"level": 2,
"id": 3,
"children": []
},
{
"question": "1.1.2",
"text_answer": null,
"level": 2,
"id": 2,
"children": [
{
"question": "1.1.2.1",
"text_answer": "SI",
"level": 3,
"id": 1,
"children": []
}
]
}
]
}
]
*/
function format(d) {
if (d.answers) {
d.answers.forEach((d) => {
format;
});
}
}
format(data);
**note:**
I changed the previous structure that I put to the question to make myself understand better.
Here is one approach via recurssion. I've commented the code to explain what exactly is going on line by line.
function _process(item) {
let result = item // we define a copy of item -> result
// this is renaming the field 'answers' (if found)
if (item.answers) {
result.children = item.answers.map(_process) // process each item separately
delete result.answers
}
// if there is a question field, extract the first item in the array and merge the attributes with result
if (item.questions) {
result = {
...result,
..._process(item.questions[0]) // also try to process the item before merging it (to check if there are nested 'questions' or 'answers' fields)
}
delete result.questions // remove the questions field
}
return result
}
function process(data) {
return data.map(_process) // start the recurssion for top-level objects
}
// sample data to test out
const data = [{
"question": "1.1",
"level": 1,
"id": 4,
"answers": [{
"text_answer": "NO",
"questions": [{
"question": "1.1.1",
"level": 2,
"id": 3,
"answers": []
}]
},
{
"text_answer": null,
"questions": [{
"question": "1.1.2",
"level": 2,
"id": 2,
"answers": [{
"text_answer": "SI",
"questions": [{
"question": "1.1.2.1",
"level": 3,
"id": 1,
"answers": []
}]
}]
}]
}
]
}]
const result = process(data)
console.log(JSON.stringify(result, null, 2))
You're on the right track in writing a recursive function.
Where I think you're going wrong is in thinking about 'pushing' it to an array or object to store the data.
Instead, what should happen is that your recursive function returns the formatted data, and then that data is added to new json object which is sent back up the call stack.
var data = {
"name": "1.question",
"answer": "YES",
"children": [{
"name": "1.1 question",
"answer": "yes"
},
{
"name": "1.2 question",
"answer": "NO",
"children": [{
"name": "1.2.1 question",
"answer": "NO"
}]
},
{
"name": "1.3 question",
"answer": "YES",
"children": [{
"name": "1.3.1 question",
"answer": "yes",
"children": [{
"name": "1.3.1.1 question",
"answer": "YES"
}]
}]
}
]
}
function format(d) {
if (d.children) {
// if there are children you are going to recurse deeper
// use the Array.prototype.map function to _transform_ each of the children.
const formattedChildren = d.children.map(v => {
return format(v);
});
// Notice that I'm returning a new object here, as well as
// An aggregation of the already transformed data
return {
data: "new formatted parent node data goes here",
children: formattedChildren
};
} else { // _always_ have a check for 'is it a leaf node'.
// If it's a leaf node node, just format it.
//Notice that I'm returning a new object here
return {
data: "new formatted leaf node data goes here"
};
}
}
console.log(format(data));
I don't know what you are trying to achieve, so I've left this blank. But this is the template for how you would recursively traverse and transform a nested object like this.
(Note that you don't need to have a data key, I've just put that in as a place holder. It looks like you want name and answered keys.
It works for me:
// here is your JSON
var json = [{
"question": "1.1",
"level": 1,
"id": 4,
"answers": [{
"text_answer": "NO",
"questions": [{
"question": "1.1.1",
"level": 2,
"id": 3,
"answers": []
}]
},
{
"text_answer": null,
"questions": [{
"question": "1.1.2",
"level": 2,
"id": 2,
"answers": [{
"text_answer": "SI",
"questions": [{
"question": "1.1.2.1",
"level": 3,
"id": 1,
"answers": []
}]
}]
}]
}
]
}]
// main
function change(branch) {
// the first level has a bit different structure
try {
if (branch.level == 1) {
return {
question: branch.question,
level: branch.level,
id: branch.id,
children: change(branch.answers)
}
}
} catch (e) {}
// next levels
var new_branch = [];
for (var i = 0; i < branch.length; i++) {
new_branch.push({
question: branch[i].questions[0].question,
text_answer: branch[i].text_answer,
level: branch[i].questions[0].level,
id: branch[i].questions[0].id,
children: [change(branch[i].questions[0].answers)].flat(1)
});
}
return new_branch;
}
// output
console.log(JSON.stringify(change(json[0])));
I have three arrays.
1. Existing viewers array - existingViewers
New viewers array - newViewers
Permitted Viewers array - permittedViewers
permittedViewers is used for rendering the drop-down. And I wish to filter the newViewers and existingViewers entries from the permittedViewers.
I am doing this as three steps. And I am afraid this is not the optimized way. Can someone suggest the ideal way of doing this?
The expected result is
[
{
"id": 4,
"name": "name4"
},
{
"id": 5,
"name": "name5"
},
{
"id": 6,
"name": "name6"
}
]
let existingViewers = [{
"viewerId": 1,
"name": "name1"
},
{
"viewerId": 2,
"name": "name2"
}
],
newViewers = [
{
"viewerId": 3,
"name": "name3"
}
],
permittedViewers = [{
"id": 1,
"name": "name1"
},
{
"id": 2,
"name": "name2"
},
{
"id": 3,
"name": "name3"
},
{
"id": 4,
"name": "name4"
},
{
"id": 5,
"name": "name5"
},
{
"id": 6,
"name": "name6"
}
]
let grouped = [...existingViewers, ...newViewers]
let viewerFilter = grouped.map(viewer => { return viewer.viewerId; });
let filteredPermittedViewers = permittedViewers.filter(viewer => !viewerFilter.includes(viewer.id));
console.log(filteredPermittedViewers)
I'd make a Set of the ids of the first two arrays, and then filter the third by whether the set includes the id. (Sets have O(1) lookup time)
let existingViewers=[{"viewerId":1,"name":"name1"},{"viewerId":2,"name":"name2"}],newViewers=[{"viewerId":3,"name":"name3"}],permittedViewers=[{"id":1,"name":"name1"},{"id":2,"name":"name2"},{"id":3,"name":"name3"},{"id":4,"name":"name4"},{"id":5,"name":"name5"},{"id":6,"name":"name6"}];
const ids = new Set([...existingViewers, ...newViewers].map(({ viewerId }) => viewerId));
const output = permittedViewers.filter(({ id }) => !ids.has(id));
console.log(output);
You can compress all three statements into a single statement -- just replace the variable name with the statement that creates it:
let existingViewers = [{
"viewerId": 1,
"name": "name1"
},
{
"viewerId": 2,
"name": "name2"
}
],
newViewers = [
{
"viewerId": 3,
"name": "name3"
}
],
permittedViewers = [{
"id": 1,
"name": "name1"
},
{
"id": 2,
"name": "name2"
},
{
"id": 3,
"name": "name3"
},
{
"id": 4,
"name": "name4"
},
{
"id": 5,
"name": "name5"
},
{
"id": 6,
"name": "name6"
}
]
let filteredPermittedViewers = permittedViewers.filter(viewer => ! [...existingViewers, ...newViewers].map(viewer => viewer.viewerId).includes(viewer.id));
console.log(filteredPermittedViewers)
So I'm trying to manipulate some rave charts using JSON. The charts are used in Cognos and it uses visJSON if that helps. I am trying to add a trend line to my bar charts but all the syntax I have found isn't working. Any help would be greatly appreciated.
"id":"dataSet",
"fields":
[
{
"id":"categories",
"label":"",
"categories":
[
"abc",
"abc",
"abc"
]
},
{
"id":"series",
"label":"",
"categories":
[
"abc",
"abc",
"abc"
]
},
{
"id":"size",
"label":"abc"
}
],
"rows":
[
[
0,
0,
1500
],
[
0,
1,
1700
],
[
0,
2,
1600
],
[
1,
0,
2400
],
[
1,
1,
2200
],
[
1,
2,
2600
],
[
2,
0,
2800
],
[
2,
1,
1600
],
[
2,
2,
1800
]
]
}
],
A tool that you'd probably find helpful in the future:
http://jsonlint.com/
I ran your JSON through it and it returned this:
Parse error on line 1:
"id":"dataSet",
^
Expecting '{', '['
The problem was your extra array bracket at the end. Here it is corrected:
{
"id": "dataSet",
"fields": [
{
"id": "categories",
"label": "",
"categories": [
"abc",
"abc",
"abc"
]
},
{
"id": "series",
"label": "",
"categories": [
"abc",
"abc",
"abc"
]
},
{
"id": "size",
"label": "abc"
}
],
"rows": [
[
0,
0,
1500
],
[
0,
1,
1700
],
[
0,
2,
1600
],
[
1,
0,
2400
],
[
1,
1,
2200
],
[
1,
2,
2600
],
[
2,
0,
2800
],
[
2,
1,
1600
],
[
2,
2,
1800
]
]
}