I'd like to use jsonpath to transform an array with 3 elements to just 2 elements.
Given this object:
var _base = {
myArray: [{ item: 1, value: "first" }, { item: 2, value: "second" }, { item: 3, value: "third" }]
}
I'd like to use jsonpath-object transform to transform the object into:
var _newBase = {
myArray: [{ newItem: 2, newValue: "second" }, { newItem: 3, newValue: "third" }]
}
I understand I could do this with a simple slice call and some object manipulation, but I have complex, variable objects that I'm transforming at runtime, so jsonpath is the requirement.
I'm able to use the below:
var transform = require('jsonpath-object-transform');
var template = { ["$.myArray", { "newItem": "$..item", "newValue": "$..value" }] }
transform(_base, _template) //emits the below
Which emits:
[{ newItem: 1, newValue: "one" }, { newItem: 2, newValue: "second" }, { newItem: 3, newValue: "third" }]
But what I really need is the above object without its first index (so just the last two objects in the array).
You can use the #path syntax to ignore the first item:
var _template = {
foo: ['$.myArray[?(#path !== "$[\'myArray\'][0]")]']
};
Returns:
{ foo: [ { item: 2, value: 'second' }, { item: 3, value: 'third' } ] }
However, it seems that jsonpath-object transform currently doesn't support mixing both #path and accessing subfields like in {["$.data", {"key": "$.value"}]}.
So this leaves you with the following options:
You can acheive what you need by using two transform operations: the first to prune out the first item, and the second to rename subfields.
slice-ing your objects beforehand, as you suggested.
And finally, patching the library (and submitting a pull request while you're at it!). It's probably a quick fix anyway, shouldn't be too time-consuming.
Related
I have an jason result as below and I want to read from it and push to specific key as below
JSON result
[
{id:1,Name:"test",billNumber:"123"}
{id:2,Name:"test1",billNumber:"123"}
{id:3,Name:"test2",billNumber:"12345"}
{id:2,Name:"test3",billNumber:"12345"}
{id:3,Name:"test4",billNumber:"12334535"}
]
I want to have array list as below
{
"123":[{id:1,Name:"test",billNumber:"123"}, {id:2,Name:"test1",billNumber:"123"}],
"12345":[ {id:3,Name:"test2",billNumber:"12345"},{id:2,Name:"test3",billNumber:"12345"}],
"12334535":[{id:3,Name:"test4",billNumber:"12334535"}]
}
How to get the above list from the json result. Please do help
You don't need lodash to do that: just a regular Array.prototype.reduce will do the work. At each iteration, you simply check if the billNumber of the current item is in the object:
if it is not (i.e. a new entry), then you assign an array with a single element
if it is (i.e. the billNumber has been encountered before), then you simply push into the array
See proof-of-concept below:
const data = [{
id: 1,
Name: "test",
billNumber: "123"
}, {
id: 2,
Name: "test1",
billNumber: "123"
}, {
id: 3,
Name: "test2",
billNumber: "12345"
}, {
id: 2,
Name: "test3",
billNumber: "12345"
}, {
id: 3,
Name: "test4",
billNumber: "12334535"
}];
const transformedData = data.reduce((acc, cur) => {
if (cur.billNumber in acc) {
acc[cur.billNumber].push(cur);
} else {
acc[cur.billNumber] = [cur];
}
return acc;
}, {});
console.log(transformedData);
Use groupBy from lodash:
const result = groupBy(input, "billNumber")
(input is your array)
you can use reduce method.
[
{id:1,Name:"test",billNumber:"123"},
{id:2,Name:"test1",billNumber:"123"},
{id:3,Name:"test2",billNumber:"12345"},
{id:2,Name:"test3",billNumber:"12345"},
{id:3,Name:"test4",billNumber:"12334535"},
].reduce((acc, value) => {
if (!acc[value.billNumber]) {
acc[value.billNumber] = [];
}
acc[value.billNumber].push(value);
return acc;
}, {})
Here is the mimic code You cann use and get help
var a = [{a:2},{a:3},{a:4}]
let b = {}
let c = 1
a.forEach(obj => {
b[c] = [obj]
c++
})
output will be
{
1: [ { a: 2 } ],
2: [ { a: 3 } ],
3: [ { a: 4 } ]
}
Thanks I hope it will help !
I have data that looks like this:
{
"_id": ObjectId("4d525ab2924f0000000022ad"),
"array": [
{ id: 1, other: 23 },
{ id: 2, other: 21 },
{ id: 0, other: 235 },
{ id: 3, other: 765 }
],
"zeroes": []
}
I'm would like to to $pull an element from one array and $push it to a second array within the same document to result in something that looks like this:
{
"_id": ObjectId("id"),
"array": [
{ id: 1, other: 23 },
{ id: 2, other: 21 },
{ id: 3, other: 765 }
],
"zeroes": [
{ id: 0, other: 235 }
]
}
I realize that I can do this by doing a find and then an update, i.e.
db.foo.findOne({"_id": param._id})
.then((doc)=>{
db.foo.update(
{
"_id": param._id
},
{
"$pull": {"array": {id: 0}},
"$push": {"zeroes": {doc.array[2]} }
}
)
})
I was wondering if there's an atomic function that I can do this with.
Something like,
db.foo.update({"_id": param._id}, {"$move": [{"array": {id: 0}}, {"zeroes": 1}]}
Found this post that generously provided the data I used, but the question remains unsolved after 4 years. Has a solution to this been crafted in the past 4 years?
Move elements from $pull to another array
There is no $move in MongoDB. That being said, the easiest solution is a 2 phase approach:
Query the document
Craft the update with a $pull and $push/$addToSet
The important part here, to make sure everything is idempotent, is to include the original array document in the query for the update.
Given a document of the following form:
{
_id: "foo",
arrayField: [
{
a: 1,
b: 1
},
{
a: 2,
b: 1
}
]
}
Lets say you want to move { a: 1, b: 1 } to a different field, maybe called someOtherArrayField, you would want to do something like.
var doc = db.col.findOne({_id: "foo"});
var arrayDocToMove = doc.arrayField[0];
db.col.update({_id: "foo", arrayField: { $elemMatch: arrayDocToMove} }, { $pull: { arrayField: arrayDocToMove }, $addToSet: { someOtherArrayField: arrayDocToMove } })
The reason we use the $elemMatch is to be sure the field we are about to remove from the array hasn't changed since we first queried the document. When coupled with a $pull it also isn't strictly necessary, but I am typically overly cautious in these situations. If there is no parallelism in your application, and you only have one application instance, it isn't strictly necessary.
Now when we check the resulting document, we get:
db.col.findOne()
{
"_id" : "foo",
"arrayField" : [
{
"a" : 2,
"b" : 1
}
],
"someOtherArrayField" : [
{
"a" : 1,
"b" : 1
}
]
}
I have unusual response from server like this
[
{
id: 1,
name: "Alexandr",
children: [
{
id: 2,
name: "Stephan"
},
{
id: 3,
name: "Nick"
}
]
},
{
id: 4,
name: "David",
children: [
{
id: 3,
name: "Nick"
},
{
id: 6,
name: "Paul"
}
]
}
]
i would like to normalize this response to receive a diction with all people. So, i use normalizr go flat this
const people= new Schema('people');
people.define({
Children: arrayOf(people),
NotOwnChildren: arrayOf(people)
});
let normalized = normalize(response.data, arrayOf(people));
but doing like this i get an error
"When merging two people, found unequal data in their "Children" values. Using the earlier value."
How can i adjust normalizr to merge people with same id (update entities with newest data)?
It looks like you're getting two people that have differing values for one of their keys (I'm assuming your example input is truncated).
For Normalizr#2:
You can use a custom mergeIntoEntity function to resolve the issue manually.
For Normalizr#>=3.0.0, you'll need use mergeStrategy.
How do I update a value inside a object inside an array. I will provide the exact array number in a variable... Heres my code:
var num = 0;
var ObjectID=require('mongodb').ObjectID;
db.collection('polls').findAndModify({
query: {_id: ObjectID(_id param)},
update: { $inc: { total: 1, "Data.chart." + num.toString + ".value": 1} }
});
This is what I want to update
{total: 0, "Data": [
{ value: 0, label: 'Beatles', color: '#4169E1' },
{ value: 0, label: 'Sting', color: '#C0C0C0' },
{ value: 0, label: 'Police', color: '#FFA500' },
{ value: 0, label: 'Journey', color: '#FF4500' },
{ value: 0, label: 'Genesis', color: '#EE82EE' }
]
}
You seem to have a subsection, chart in the query that does not exist in the actual data to update. Just eliminate that;
db.collection('polls').findAndModify({
query: {_id: ObjectID(_id param)},
update: { $inc: { total: 1, "Data." + num.toString + ".value": 1} }
});
turns the query into
db.polls.update({}, { $inc: { total: 1, "Data.2.value": 1} })
resulting in
db.polls.find().pretty()
{
"_id" : ObjectId("56cbebb6ef788d178e2dfdc0"),
"total" : 1,
"Data" : [
...
{
"value" : 1,
"label" : "Police",
"color" : "#FFA500"
},
...
JavaScript "stringifies" presented keys, so this is why you are not getting what you expect.
To use a variable as part of a "calculated key name" you need to use the bracket [] notation to define the object:
var num = 0;
var update = { "$inc": { "total": 1 } };
update["$inc"]["Data." + num + ".value"] = 1;
db.collection('polls').findAndModify({
query: { _id: ObjectID(_id param) },
update: update
});
Better yet, you should not presume the "index" that you think you want to modify is the actual location of the data. Instead match the array element in the query and use the positional $ operator instead of a hard index:
db.collection('polls').findAndModify({
query: { "_id": ObjectID(_idParam), "Data.label": 'Beatles' },
update: { "$inc": { "total": 1, "Data.$.value": 1 } }
});
So the element that matches Data.label as "Beatles" will be the one updated, and regardless of the current index position in the array.
That means "scalable", where other writes could alter the array content but you are still updating the one you want, even if it changed position due to another update.
N.B The document in your question has the path to array elements as Data and not Data.chart. But apply however your real data is contructed.
In MongoDB - how do you retrieve only the first item of an array which is in a property?
I have a nested document and query it with $text (but that doesn't matter, normal queries don't work either)
The structure of my document:
{
"_id": ObjectId("...."),
"propA": {
"prop1": [
{ ... }, // this is what I want to see
{ ... },
...
],
"prop2": { ... },
"prop3": { ... },
...
},
"propB": {
"prop1": { ... }, // +this, but that's not a problem
"prop2": { ... },
"prop3": { ... },
...
},
"propC": { ... },
...
}
when I run
collection.find({}, { "propA.prop1": 1, "propB.prop2": 1 });
I get the full array at propA.prop1. When I run instead
collection.find({}, { "propA.prop1": {$slice: 1}, "propB.prop2": 1 });
I get only the first item of propA.prop1 but I get also all the other items inside propA (like propA.prop2, propA.prop3, ...)
I'd want to somehow combine the two queries, but couldn't figure out how (except after retrieving in code).
You can work around this by including a second, non-existent field of propA in your projection:
collection.find({}, {
"propA.prop1": {$slice: 1},
"propA.nonExistentField": 1,
"propB.prop2": 1
});
A bit odd, but it works.