Intersection of arrays of objects based on object property value - coffeescript

array1 = [ { id: "a", score: 1 }, { id: "b", score: 3 }, { id: "c", score: 8 }]
array2 = [ { id: "a", score: 2 }, { id: "z", score: 5 }, { id: "c", score: 1 }]
array3 = [ { id: "a", score: 3 }, { id: "f", score: 2 }, { id: "c", score: 2 }]
How can I get a result array containing only objects that have an id property value that exists and is the same in all arrays ? ie:
resultyArray = [
{ id: "a", score: 1 },
{ id: "a", score: 2 },
{ id: "a", score: 3 },
{ id: "c", score: 8 },
{ id: "c", score: 1 },
{ id: "c", score: 2 }
]

Let's say that the objects whose ids appear on every array are "ubiquitous" (naming things is hard =P):
filterUbiquitousObjects = (arrays...) ->
objectsById = {}
(objectsById[obj.id] or= []).push obj for obj in arr for arr in arrays
[].concat (arr for id, arr of objectsById when arr.length is arrays.length)...
array1 = [{ id: "a", score: 1 }, { id: "b", score: 3 }, { id: "c", score: 8 }]
array2 = [{ id: "a", score: 2 }, { id: "z", score: 5 }, { id: "c", score: 1 }]
array3 = [{ id: "a", score: 3 }, { id: "f", score: 2 }, { id: "c", score: 2 }]
console.log filterUbiquitousObjects array1, array2, array3
# Output:
# [{id:"a", score:1}, {id:"a", score:2}, {id:"a", score:3}, {id:"c", score:8},
# {id:"c", score:1}, {id:"c", score:2}]
The nested for loops are a bit ugly, but this should be O(n) (being n the total number of objects) unless i screwed up somewhere.
Update: you can also do:
res = []
for id, arr of objectsById when arr.length is arrays.length
res = res.concat arr
res
Instead of the cryptic [].concat (arr for id, arr of objectsById when arr.length is arrays.length)... line. It's arguably more readable and will generate saner JS :)

Related

mongoDB filter, sort, and rank result

I have a collection like this
{
id: 1,
category: "food",
score: 20
}
{
id: 2,
category: "drink",
score: 19
}
{
id: 3,
category: "food",
score: 50
}
{
id: 4,
category: "food",
score: 30
}
id is not unique btw.
but it is unique in that category.
so it is possible to have
{id: 1, category: "food"}
{id: 1, category: "drink"}
but not
{id: 1, category: "food"}
{id: 1, category: "food"}
here's what I want to do
find all category == "food"
-> it will give id: 1, 3, 4
// I can add some other filter here before sort happen
// like id less than 100
sort them by score
-> it will give id: 3, 4, 1 // highest score must be the first entry
then what is the rank of id: [4, 1]
-> it should give me {id: 4, rank: 2}, {id: 1, rank: 3}
how can I achieve this?
please give me some snippets or idea
db.collection.aggregate([
{
"$match": { //Filter conditions
"category": "food"
}
},
{
"$sort": {//Sorting
"score": -1
}
},
{
"$group": { //Group by null to get array index
"_id": "null",
"data": {
"$push": "$$ROOT",
}
}
},
{
"$unwind": { //Unwind and get index
path: "$data",
"includeArrayIndex": "index"
}
},
{
"$match": {
"data.id": { //Filter require ids
$in: [
3,
4
]
}
}
}
])
Sample

How do I get the last object array in MongoDB based on condition

I want to query mongoDB Data:
mongoDBData:
[{ cost: 1, productCode: "A"}],
[{ cost: 2, productCode: "A"}],
[{ cost: 3, productCode: "B"}],
[{ cost: 4, productCode: "A"}],
[{ cost: 5, productCode: "B"}],
[{ cost: 6, productCode: "A"}],
[{ cost: 7, productCode: "C"}],
[{ cost: 8, productCode: "C"}],
[{ cost: 9, productCode: "D"}],
[{ cost: 10, productCode: "D"}]
based on an array. This is the array:
mappedProductCode = ["A", "B", "C"]
This is my desired result:
desiredResult = [
{productCode: "A", cost: 6},
{productCode: "B", cost: 5},
{productCode: "C", cost: 8},
]
Here's how I did by making a loop:
productCost=[]
for (let i = 0; i < mappedProductCode.length; i++) {
const skuLoop = mappedProductCode[i];
const skuCost = await PosCost.findOne({ productCOde: mappedProductCode[i] }).sort({ _id: -1 }).limit(1);
const loopPrice = skuCost? skuCost.cost : 0;
productCost[i] = {
sku: skuLoop,
cost: loopPrice
};
}
My desired result is still achieved but it is very slow, what do I do to improve the code?
Maybe something like this:
db.collection.aggregate([
{
$match: {
productCode: {
$in: [
"A",
"B",
"C"
]
}
}
},
{
$sort: {
productCode: 1,
cost: -1
}
},
{
$group: {
_id: "$productCode",
cost: {
$first: "$cost"
}
}
},
{
$project: {
productCode: "$_id",
cost: 1,
_id: 0
}
},
{
$sort: {
productCode: 1
}
}
])
Explained:
$match the products that you need
$sort by productCode and descending cost
$group by productCode and get only the first cost from the descending order
$project the _id to the original key name "productCode"
$sort by needed final result order
playground

Merge 2 array objects into 1

I am trying to merge objects within an array based on another array. What I have is,
Orders
{
_id: 0,
orderID: 1,
entries: [
{ item_id: 43, quantity: 2 },
{ item_id: 2, quantity: 1}
]
}
Items
{
_id: 43,
item_name: "tshirt"
}
{
_id: 2,
item_name: "jeans"
}
After lookup I am getting the below document with 2 arrays - entries and items. I would like to have entries contain the corresponding item.
{
_id: 0,
orderID: 1,
entries: [
{ item_id: 43, quantity: 2 },
{ item_id: 2, quantity: 1}
]
items: [
{ item_id: 43, item_name: "tshirt" },
{ item_id: 2, item_name: "jeans" },
]
}
Desired Output:
{
_id: 0,
orderID: 1,
entries: [
{ item_id: 43, quantity: 2, item_name: "tshirt" },
{ item_id: 2, quantity: 1, item_name: "jeans"}
]
}
I was able to achieve this by unwinding both arrays, addFields and then grouping as mentioned by #whoami.
See current pipeline: https://mongoplayground.net/p/rL-Lzmfuw9h
Is there any way to achieve this without unwinding?

Query Documents In Array

I have one embedded MongoDB document and I need to remove {nameofcontent:"miljox"} from the below-mentioned document.
{
_id: 1,
results: [
{ item: "A", score: 5, answers: [ { q:[{nameofcontent:"miljox"}]: 1, a: 4 }, { q:[{nameofcontent:"miljo"}], a: 6 } ] },
{ item: "B", score: 8, answers: [ { q:[{nameofcontent:"y"}], a: 8 }, { q: [{nameofcontent:"anil"}], a: 9 } ] }
]
}

Order Mongoose/MongoDB query results by number of values not in an array

I have two collections with the following simplified schemas:
// Ingredient
{
_id: Number
}
// Recipe
{
_id: Number,
ingredients: [{
type: Number,
ref: 'Ingredient'
}]
}
I'm trying to figure out how to implement a search query for recipes based on what ingredients you have available, sorted by the number of ingredients missing from each recipe.
For example, if I have the following data:
// Ingredients
{
_id: 1
},
{
_id: 2
},
{
_id: 3
},
{
_id: 4
},
{
_id: 5
}
// Recipes
{
_id: 1,
ingredients: [1, 2, 5]
},
{
_id: 2,
ingredients: [2, 4]
},
{
_id: 3,
ingredients: [2, 3]
}
and I input ingredients 2 and 3, the expected results would be
{
_id: 3,
ingredients: [2, 3] // Missing 0 ingredients
},
{
_id: 2,
ingredients: [2, 4] // Missing 1 ingredient
},
{
_id: 1,
ingredients: [1, 2, 5] // Missing 2 ingredients
}
Is it possible to do this with a query alone?
You can do this using $setDifference to find the missing ingredients, and then $size to get their count that you can then $sort on.
var ingredients = [2, 3];
db.recipes.aggregate([
{$project: {missing: {$setDifference: ['$ingredients', ingredients]}}},
{$project: {missing: 1, numMissing: {$size: '$missing'}}},
{$sort: {numMissing: 1}}
])
Results:
{ "_id" : 3, "missing" : [ ], "numMissing" : 0 }
{ "_id" : 2, "missing" : [ 4 ], "numMissing" : 1 }
{ "_id" : 1, "missing" : [ 1, 5 ], "numMissing" : 2 }