I have data in following form. And I am using loadash for groupby by row. However loadash groupby is returning groups based on row values, I am expecting it should ignore minor difference of 1 value between rows and then group. ex. it should add items with row 9 and row 10 in one group. What should I return from groupby callback function which will groupby on properties by including neighboring values.
[
{
"row": 9,
"city": "Camas"
},
{
"row": 9,
"city": "Kersey"
},
{
"row": 6,
"city": "Ebro"
},
{
"row": 10,
"city": "Orick"
},
{
"row": 2,
"city": "Bonanza"
},
{
"row": 6,
"city": "Rowe"
},
{
"row": 5,
"city": "Walland"
}
]
Expected Output
{
2: [
{
"row": 2,
"city": "Bonanza"
},
],
5: [
{
"row": 6,
"city": "Ebro"
},
{
"row": 6,
"city": "Rowe"
},
{
"row": 5,
"city": "Walland"
}
],
9: [{
"row": 9,
"city": "Camas"
},
{
"row": 9,
"city": "Kersey"
},
{
"row": 10,
"city": "Orick"
}
]
}
A way to solve this problem is to sort the collection by row in ascending order, and then reduce the sorted collection by combining items with a row difference of one and simply assigning those who doesn't have one.
var result = _(data)
.sortBy('row')
.reduce(function(group, item) {
var nIndex = item.row - 1;
var value = [item];
if (group[nIndex]) {
group[nIndex] = value.concat(group[nIndex]);
} else {
group[item.row] = value;
}
return group;
}, {});
var data = [{
"row": 9,
"city": "Camas"
},
{
"row": 9,
"city": "Kersey"
},
{
"row": 6,
"city": "Ebro"
},
{
"row": 10,
"city": "Orick"
},
{
"row": 2,
"city": "Bonanza"
},
{
"row": 6,
"city": "Rowe"
},
{
"row": 5,
"city": "Walland"
}
];
var result = _(data)
.sortBy('row')
.reduce(function(group, item) {
var nIndex = item.row - 1;
var value = [item];
if (group[nIndex]) {
group[nIndex] = value.concat(group[nIndex]);
} else {
group[item.row] = value;
}
return group;
}, {});
console.log(result);
body>div {
min-height: 100%;
top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
The solution above can also be implemented using vanilla JS Array#sort and Array#reduce
var result = data
.sort(function(v1, v2) {
return v1.row - v2.row;
})
.reduce(function(group, item) {
var nIndex = item.row - 1;
var value = [item];
if (group[nIndex]) {
group[nIndex] = value.concat(group[nIndex]);
} else {
group[item.row] = value;
}
return group;
}, {});
var data = [{
"row": 9,
"city": "Camas"
},
{
"row": 9,
"city": "Kersey"
},
{
"row": 6,
"city": "Ebro"
},
{
"row": 10,
"city": "Orick"
},
{
"row": 2,
"city": "Bonanza"
},
{
"row": 6,
"city": "Rowe"
},
{
"row": 5,
"city": "Walland"
}
];
var result = data
.sort(function(v1, v2) {
return v1.row - v2.row;
})
.reduce(function(group, item) {
var nIndex = item.row - 1;
var value = [item];
if (group[nIndex]) {
group[nIndex] = value.concat(group[nIndex]);
} else {
group[item.row] = value;
}
return group;
}, {});
console.log(result);
body>div {
min-height: 100%;
top: 0;
}
Related
Recently I'm working with a Mongodb database. Here is the data model of the document I need to run a query on:
{
"creation_date": {
"$date": {
"$numberLong": "1641981205813"
}
},
"user_id": {
"$oid": "61dedd8b7a520461dd78016b"
},
"products": [
{
"_id": {
"$oid": "61dede397a520461dd7818bd"
},
"product_id": {
"$oid": "615071ae8b66e1e9a3d6ea50"
},
"payment": true,
"support_all_payment": false,
"term_ids": null
}
],
"carts_info": [
{
"_id": {
"$oid": "61dede397a520461dd7818be"
},
"support_type": null,
"support_price": 0,
"product_price": 11000,
"product_type": "all",
"final_price": 11000,
"product_id": {
"$oid": "615071ae8b66e1e9a3d6ea50"
}
}
],
"_des": "initial_payment",
"_type": "online",
"_token": "9e0cb4d111f642f1a6f482bb04f1f57b",
"_price": 11000,
"_status": "unpaid",
"_terminal_id": "12605682",
"__v": 0,
"additional_information": {
"saman_bank": {
"MID": "0",
"ResNum": "61dede387a520461dd7818bb",
"State": "CanceledByUser",
"TraceNo": "",
"Wage": "",
"Rrn": "",
"SecurePan": "",
"HashedCardNumber": "",
"Status": "1"
}
}
}
This collection is user orders. I need to count the orders for today. So, I need such a equivalent query for Mongodb Compass the same as this SQL:
SELECT count(1) num,
date(creation_date) date
FROM orders
WHERE date(creation_date) = "2023-02-16"
GROUP BY date
Any idea how can I run this logic on Mongodb Compass?
Use $dateTrunc to perform date only operations.
db.collection.aggregate([
{
"$match": {
$expr: {
$eq: [
{
$dateTrunc: {
date: "$creation_date",
unit: "day"
}
},
ISODate("2022-01-12")
]
}
}
},
{
$group: {
_id: {
$dateTrunc: {
date: "$creation_date",
unit: "day"
}
},
num: {
$sum: 1
}
}
}
])
Mongo Playground
For OP's MongoDB v3.6, we can use $dateToString to perform string comparison on a date-only string.
db.collection.aggregate([
{
$addFields: {
dateOnly: {
"$dateToString": {
"date": "$creation_date",
"format": "%Y-%m-%d"
}
}
}
},
{
$match: {
dateOnly: "2022-01-12"
}
},
{
$group: {
_id: null,
num: {
$sum: 1
}
}
}
])
Mongo Playground
I'm pretty sure the below can be done, I'm struggling to understand how to do it in MongoDB.
My data is structured like this (demo data):
db={
"recipes": [
{
"id": 1,
"name": "flatbread pizza",
"ingredients": {
"1010": 1,
"1020": 2,
"1030": 200
}
},
{
"id": 2,
"name": "cheese sandwich",
"ingredients": {
"1040": 1,
"1050": 2
}
}
],
"ingredients": [
{
"id": 1010,
"name": "flatbread",
"unit": "pieces"
},
{
"id": 1020,
"name": "garlic",
"unit": "clove"
},
{
"id": 1030,
"name": "tomato sauce",
"unit": "ml"
},
{
"id": 1040,
"name": "bread",
"unit": "slices"
},
{
"id": 1050,
"name": "cheese",
"unit": "slices"
}
]
}
The output I'm trying to achieve would look like this:
[
{
"id": 1,
"name": "flatbread pizza",
“flatbread”: “1 pieces”,
“garlic”: “2 cloves”,
“tomato sauce”: “200 ml”
},
{
"id": 2,
"name": "cheese sandwich",
“bread”: “1 slices”,
“cheese”: “2 slices”
}
]
I've tried several approaches, and I get stuck at the bit where I need to do a lookup based on the ingredient name (which actually is the id). I tried using $objectToArray to turn it into a k-v document, but then I get stuck in how to construct the lookup pipeline.
This is not a simple solution, and probably can be improved:
db.recipes.aggregate([
{
"$addFields": {
ingredientsParts: {
"$objectToArray": "$ingredients"
}
}
},
{
$unwind: "$ingredientsParts"
},
{
"$group": {
_id: "$id",
name: {
$first: "$name"
},
ingredientsParts: {
$push: {
v: "$ingredientsParts.v",
id: {
$toInt: "$ingredientsParts.k"
}
}
}
}
},
{
"$lookup": {
"from": "ingredients",
"localField": "ingredientsParts.id",
"foreignField": "id",
"as": "ingredients"
}
},
{
$unwind: "$ingredients"
},
{
"$addFields": {
"ingredientsPart": {
"$filter": {
input: "$ingredientsParts",
as: "item",
cond: {
$eq: [
"$$item.id",
"$ingredients.id"
]
}
}
}
}
},
{
$project: {
ingredients: 1,
ingredientsPart: {
"$arrayElemAt": [
"$ingredientsPart",
0
]
},
name: 1
}
},
{
"$addFields": {
units: {
k: "$ingredients.name",
v: {
"$concat": [
{
$toString: "$ingredientsPart.v"
},
" ",
"$ingredients.unit"
]
}
}
}
},
{
$group: {
_id: "$_id",
name: {
$first: "$name"
},
units: {
$push: "$units"
}
}
},
{
"$addFields": {
"data": {
"$arrayToObject": "$units"
}
}
},
{
"$addFields": {
"data.id": "$_id",
"data.name": "$name"
}
},
{
"$replaceRoot": {
"newRoot": "$data"
}
}
])
You can see it works here
As rickhg12hs said, it can be modeled better.
I am trying to merge arrays of objects into one clean array using Ramda but I need some help. I have sample JSON which is below. In this example, I have 2 groups but the number of groups can be 3, 4, 10. I am interested in tableItems array from each group.
const groups = [
{
id: '',
name: '',
tableItems: [
{
id: 1,
name: 'John'
},
{
id: 2,
name: 'Paul'
},
{
id: 3,
name: 'Mary'
}
]
},
{
id: '',
name: '',
tableItems: [
{
id: 10,
name: 'Brian'
},
{
id: 20,
name: 'Joseph'
},
{
id: 30,
name: 'Luke'
}
]
}
];
I tried something like this:
let mapValues = x => x.tableItems;
const testItems = R.pipe(
R.map(mapValues)
)
And then I got arrays of my tableItems and now I would like to merge them into one array.
[
[
{
"id": 1,
"name": "John"
},
{
"id": 2,
"name": "Paul"
},
{
"id": 3,
"name": "Mary"
}
],
[
{
"id": 10,
"name": "Brian"
},
{
"id": 20,
"name": "Joseph"
},
{
"id": 30,
"name": "Luke"
}
]
]
Any help would be appreciated.
Use R.chain to map and flatten, and get the tableItems using R.prop:
const fn = R.chain(R.prop('tableItems'));
const groups = [{"id":"","name":"","tableItems":[{"id":1,"name":"John"},{"id":2,"name":"Paul"},{"id":3,"name":"Mary"}]},{"id":"","name":"","tableItems":[{"id":10,"name":"Brian"},{"id":20,"name":"Joseph"},{"id":30,"name":"Luke"}]}];
const result = fn(groups);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
I want to transform this array of objects using RamdaJS. From this array of objects
let children = [
{ "name": "Bob", "age": 8, "father": "Mike" },
{ "name": "David", "age": 10, "father": "Mike" },
{ "name": "Amy", "age": 2, "father": "Mike" },
{ "name": "Jeff", "age": 11, "father": "Jack" }
]
into this array of objects
let fatherAndKids = [
{
"father": "Mike",
"count" : 3,
"kids": [
{ "name": "Bob", "age": 8 },
{ "name": "David", "age": 10 },
{ "name": "Amy", "age": 2
}
]
},
{
"father": "Jack",
"count" : 1,
"kids": [
{ "name": "Jeff", "age": 11 }
]
}
]
Here's what i did so far. But i failed to remove the father keys from kids's array
R.pipe(
R.groupBy(R.prop('father')),
R.map(kids => ({
father: R.head(kids)["father"],
count: kids.length,
kids: kids
})),
R.values()
)(children)
Use R.applySpec to create the object, and use R.map with R.dissoc to remove the 'father' property:
const { pipe, groupBy, prop, applySpec, head, length, map, dissoc, values } = R
const fn = pipe(
groupBy(prop('father')),
map(applySpec({
father: pipe(head, prop('father')),
count: length,
kids: map(dissoc('father'))
})),
values
)
const children = [
{ "name": "Bob", "age": 8, "father": "Mike" },
{ "name": "David", "age": 10, "father": "Mike" },
{ "name": "Amy", "age": 2, "father": "Mike" },
{ "name": "Jeff", "age": 11, "father": "Jack" }
]
const result = fn(children)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
I have an object with an array of images (just the ids).
var listOfImages = {
"number-items": 30,
"pageCount": 0,
"ids": {
0: "image1",
1: "image2"
}
};
I also have a collection of images:
var images = {
"image1": {
"author": "Marc",
"size": "40kb"
},
"image2": {
"author": "Anthony",
"size": "60kb"
},
"image3": {
"author": "Anthony",
"size": "60kb"
}
}; image2,
]
I would like to combine them all so having something like this:
var extendedListOfImages = {
"number-items": 30,
"pageCount": 0,
"ids": {
0: {"id": "image1", "author": "Marc", "size": "40kb"},
1: {"id": "image2", "author": "Anthony", "size": "60kb"}
}
};
This is my code:
https://jsbin.com/pamofudisa/edit?html,js,console,output
New to lodash so I would appreciate a better solution if possible.
My solution right now:
var listOfImages = {
"number-items": 30,
"pageCount": 0,
"ids": {
0: "image1",
1: "image2"
}
};
var images = {
"image1": {
"author": "Marc",
"size": "40kb"
},
"image2": {
"author": "Anthony",
"size": "60kb"
},
"image3": {
"author": "Anthony",
"size": "60kb"
}
};
myIds = _.keyBy(listOfImages.ids);
_.map(myIds, function(key, value){
myIds[key]= images[key];
});
listOfImages.ids=myIds;
console.log(listOfImages);
The code in jsbin:
https://jsbin.com/qesuyewuza/1/edit?html,js,console,output