How to groupby using lodash by ignoring minor difference - lodash

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

How to group by in MongoDB Compass based on the time?

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

MongoDB Lookup values based on dynamic field name

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.

Ramda - how to merge 2 or more arrays of objects

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>

RamdaJS groupBy and tranform object

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>

Extend one object with another using lodash

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