How to get least(min) value from array of object? - node.js

I have a an array that contains list of json object like mentioned below:
[
{
"id":1,
"type":"type1",
"selling_price":2199,
},
{
"id":2,
"type":"type1",
"selling_price":4999,
},
{
"id":3,
"type":"type1",
"selling_price":6999,
},
{
"id":4,
"type":"type2",
"selling_price":1999,
},
{
"id":5,
"type":"type2",
"selling_price":2399,
},
{
"id":6,
"type":"type2",
"selling_price":2999,
}
]
I want to extract min selling price for only type="type1". the min price should be 2199 for type1
I tried using below method but its not working
_.map(arr, price => {
if(price.type=="type1") {
let pp =_.min(price, function(minPrice){ return minPrice.selling_price; });
console.log(pp)
}
});
Edit code suggested by #VLAZ
data() {
return {
minSellingPrice:'',
arr:[]
}
},
method() {
leastPrice() {
if(this.arr) {
const result = _.chain(arr)
.filter(item => item.type === "type1")
.map('selling_price')
.min()
.value()
this.minSellingPrice=result;
}
else this.minSellingPrice=''
}
}
by default the this.minSellingPrice should be empty but with this code whenever this.minSellingPrice is showing as infinity.

You are doing the mapping wrong. You have the condition inside the callback which only gives you one item at a time. Instead, you should filter() the collection to only the type you want first, then get the min() price.
Underscore has the _.chain() method which allows you to do multiple operations on the same dataset without needing to save intermediate results. To get the result at the end, you need to call .value() to signify no further calls would be chained:
const arr = [ { "id":1, "type":"type1", "selling_price":2199, }, { "id":2, "type":"type1", "selling_price":4999, }, { "id":3, "type":"type1", "selling_price":6999, }, { "id":4, "type":"type2", "selling_price":1999, }, { "id":5, "type":"type2", "selling_price":2399, }, { "id":6, "type":"type2", "selling_price":2999, } ];
const result = _.chain(arr)
.filter(item => item.type === "type1")
.min('selling_price')
.value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.11.0/underscore-min.js"></script>
If you want just the price, not the object itself, you can use map() to extract just that property:
const arr = [ { "id":1, "type":"type1", "selling_price":2199, }, { "id":2, "type":"type1", "selling_price":4999, }, { "id":3, "type":"type1", "selling_price":6999, }, { "id":4, "type":"type2", "selling_price":1999, }, { "id":5, "type":"type2", "selling_price":2399, }, { "id":6, "type":"type2", "selling_price":2999, } ];
const result = _.chain(arr)
.filter(item => item.type === "type1")
.map('selling_price')
.min()
.value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.11.0/underscore-min.js"></script>
If you want a fallback value in case of an empty array, currently the code will return Infinity, since this is the default return of min. Unfortunately, Underscore doesn't have a comprehensive enough API to easily solve it. One easy way is to just check for that value and substitute it with another for example.
if (result === Infinity) {
result = null;
}
An alternative if you want to use only Undescore is to define your own method via mixin(). Many methods might solve the issue here but as a general helper method, I'd go for or() which will allow you to swap a value with another:
/**
* Generic function that either value or if the value fails a test a fallback
* #param {*} value - value to be tested or substituted
* #param {*} otherValue - fallback in case `value` fails the test
* #param {Function} [test] - what the value would be tested against. By default it fails for null or undefined
*/
_.mixin({
or: function(value, otherValue, test = x => x != null ) {
return test(value) ? value : otherValue ;
}
});
const fullArray = [ { "id":1, "type":"type1", "selling_price":2199, }, { "id":2, "type":"type1", "selling_price":4999, }, { "id":3, "type":"type1", "selling_price":6999, }, { "id":4, "type":"type2", "selling_price":1999, }, { "id":5, "type":"type2", "selling_price":2399, }, { "id":6, "type":"type2", "selling_price":2999, } ];
const emptyArray = [];
function getMinimum(arr) {
return _.chain(arr)
.filter(item => item.type === "type1")
.map('selling_price')
.min()
.or("", _.isFinite) //use the or() mixin and pass a different test function that rejects `Infinity`
.value();
}
const fullResult = getMinimum(fullArray);
const emptyResult = getMinimum(emptyArray);
console.log(`fullResult is [${fullResult}]`);
console.log(`emptyResult is [${emptyResult}]`); //empty string
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.11.0/underscore-min.js"></script>
Lastly, if you like Underscore but find the API a bit limiting, like in this case, I'd suggest switching to Lodash. It is very close to Undescore as it splintered off at some point in the past. Lodash has a bit more consistent interface and a lot more functionality, so it a lot of cases it's a straight upgrade over Underscore or at least on par. The functionality you want expressed in Lodash is this:
const fullArray = [ { "id":1, "type":"type1", "selling_price":2199, }, { "id":2, "type":"type1", "selling_price":4999, }, { "id":3, "type":"type1", "selling_price":6999, }, { "id":4, "type":"type2", "selling_price":1999, }, { "id":5, "type":"type2", "selling_price":2399, }, { "id":6, "type":"type2", "selling_price":2999, } ];
const emptyArray = [];
function getMinimum(arr) {
return _.chain(arr) //no need for .chain() as Lodash implicitly chains via this
.filter(item => item.type === "type1")
.map('selling_price')
.min() //min defaults to `undefined` if nothing is found
.defaultTo(''); // similar to the `or()` mixin above but only for `undefined`, `null`, or `NaN`
.value()
}
const fullResult = getMinimum(fullArray);
const emptyResult = getMinimum(emptyArray);
console.log(`fullResult is [${fullResult}]`);
console.log(`emptyResult is [${emptyResult}]`); //empty string
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
Lodash supports everything needed out of the box to complete the entire operation, unlike Underscore.

You use the function minBy and filter function of lodash
let filter_arr = _.filter(arr, function(o) { return o.type=='type1';})
_.minBy(filter_arr,function(o) {
return o.selling_price;
});
It will give as below result
{id: 1, selling_price: 2199, type: "type1"}
Hope it will help you

Related

Mongoose multiple optional 'and' conditions

I am trying to filter a collection with 3 optional 'and' conditions.
Here is my model:
const Company = mongoose.model(
'Company',
new Schema({
name: { type: String },
sectors: [{ type: Schema.Types.ObjectId, ref: 'Sector' }],
industries: [{ type: Schema.Types.ObjectId, ref: 'Industry' }],
countries: [{ type: Schema.Types.ObjectId, ref: 'Country' }],
})
And my component:
const getCompanies = (skip, limit, filter) =>
Company.find({
...filter.countries && { countries: filter.countries },
...filter.sectors && { sectors: filter.sectors },
...filter.industries && { industries: filter.industries },
})
.skip(skip)
.limit(limit)
.sort({ date: -1 })
.populate('countries')
.populate('sectors')
.populate('industries');
const getAll = async (req, res) => {
try {
const countries = req.query.country;
const sectors = req.query.sector;
const industries = req.query.industry;
const skip = parseInt(req.query.skip, 10);
const limit = parseInt(req.query.limit, 10);
const filter = {
...countries && { countries },
...sectors && { sectors },
...industries && { industries },
};
const result = await getCompanies(skip, limit, filter);
return res.status(200).json(result);
} catch (e) {
return res.status(500).send({ message: (e.message) });
}
};
This is working when the filter is empty, but when there is one or more items in the filter, I get an empty array.
If I hard code data in getCompanies like so:
Company.find({
countries: '5d5e913e20c01070fef5c77e',
sectors: '5d5e913e20c01070fef5c754',
industries: '5d5e913e20c01070fef5c7ad',
})
or :
Company.find({
countries: '5d5e913e20c01070fef5c77e'
})
I get the data I want.
I also tried to console.log the filter in getCompanies to make sure the data was correct, and I get this if all fields are requested:
{
countries: '5d5e913e20c01070fef5c77e',
sectors: '5d5e913e20c01070fef5c754',
industries: '5d5e913e20c01070fef5c7ad',
}
and this for just one:
{ countries: '5d5e913e20c01070fef5c77e' }
So it seems fine to me.
I also tried using '$and' like so:
Company.find({ $and: [
{ ...filter.countries && { countries: filter.countries } },
{ ...filter.sectors && { sectors: filter.sectors } },
{ ...filter.industries && {industries: filter.industries } },
],
})
or using '$in':
Company.find({
...filter.countries && { countries: { $in: filter.countries } },
...filter.sectors && { sectors: { $in: filter.sectors } },
...filter.industries && { industries: { $in: filter.industries } },
})
But no luck either.
Here is a sample URL:
GET /api/internal/member/get?skip=12&limit=6&country=5d5e913e20c01070fef5c77e&sector=&industry=
I have found some other threads with questions somewhat similar to mine, but they were to different to help me solve my case.
Looking forward to your helpful advice.
I finally got it to work. Turns out I was not clearing the data in the frontend at each change, which caused an issue with the skip/limit fields.
I also changed the find as advised by #whoami like so:
Company.find({ $and: [
{
...filter.countries && { countries: { $in: [mongoose.Types.ObjectId(filter.countries)] } },
...filter.sectors && { sectors: { $in: [mongoose.Types.ObjectId(filter.sectors)] } },
...filter.sdgs && { sdgs: { $in: [mongoose.Types.ObjectId(filter.sdgs)] } },
}
],
})

How to call recursive function after execute async.each

Anyone suggests me how can I trigger recursive function after executing the async.each.I have done the logic by following
function recursive(aggs, callback) {
async.each(datas, function(data, asynccallback) {
// after some process
asynccallback(null, true);
}, function(err, resultData) {
//after execute the async.each I need to do the following
if (resultData.flage) {
// flage true call recirsive
recursive(args, callback);
} else {
// flage flase means return the callback to called function
callback(null, data)
}
});
}
When I try the above way I got
error: callback was already called.
Can anyone guide me to achieve this?.Thanks in Advance
Maybe try Lodash-recursive :
https://www.npmjs.com/package/lodash-recursive
They provided you an example :
var assert = require('assert')
var recursive = require('lodash-recursive')
var nodes = [
{
value: 'alpha',
children: [
{
value: 'beta'
}
]
},
{
value: 'gamma'
}
]
var newNodes = recursive.map(nodes, function (node, recursive, map) {
if (node.children) recursive(node.children)
return map(node)
}, function (node) {
if (node.value == 'beta') node.valid = true
return node
})
var expected = [
{
"value": "alpha",
"children": [
{
"value": "beta",
"valid": true
}
]
},
{
"value": "gamma"
}
]
assert.deepEqual(expected, newNodes)

Abstract type Node must resolve to an Object type at runtime for field Root.node with value \"\",received \"null\"."

I am implementing a search feature with react and relay.
Below is my schema.js
var { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
var { type, id } = fromGlobalId(globalId);
if (type === 'User') {
return getUser(id);
}else if (type === 'Post') {
return getPost(id);
}else if (type === 'Setting') {
return getSetting(id);
}
return null;
},
(obj) => {
if (obj instanceof User) {
return userType;
}else if (obj instanceof Post) {
return postType;
}else if (obj instanceof Setting) {
return settingType;
}
return null;
}
);
var postType = new GraphQLObjectType({
name: 'Post',
fields: {
_id: {
type: new GraphQLNonNull(GraphQLID)
},
createdAt: {
type: GraphQLString
},
id: globalIdField('Post'),
title: {
type: GraphQLString
},
color: {
type: GraphQLString
},
userId: globalIdField('User'),
username: {
type: GraphQLString,
resolve: (post) => getUserById(post.userId),
},
content: {
type: GraphQLString
},
images: {
type: postImageType,
description: "Post's main image links"
}
},
interfaces: [nodeInterface]
});
const {
connectionType: postConnection,
} = connectionDefinitions({name: 'Post', nodeType: postType});
var settingType = new GraphQLObjectType({
name: 'Setting',
fields: {
_id: {
type: new GraphQLNonNull(GraphQLID)
},
id: globalIdField('Setting'),
amount: {
type: GraphQLString
},
all_posts: {
type: postConnection,
args: {
...connectionArgs,
query: {type: GraphQLString}
},
resolve: (rootValue, args) => connectionFromPromisedArray(
getAllPosts(rootValue, args),
args
),
},
},
interfaces: [nodeInterface]
});
var Root = new GraphQLObjectType({
name: 'Root',
fields: () => ({
node: nodeField,
setting: {
type: settingType,
args: {
...connectionArgs,
currency: {type: GraphQLString}
},
resolve: (rootValue, args) => {
return getSetting(args.currency).then(function(data){
return data[0];
}).then(null,function(err){
return err;
});
}
},
})
});
Below is my database.js
export function getAllPosts(params,args) {
let findTitle = {};
let findContent = {};
if (args.query) {
findTitle.title = new RegExp(args.query, 'i');
findContent.content = new RegExp(args.query, 'i');
}
console.log("getAllPosts",args)
return new Promise((resolve, reject) => {
Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec({}, function(err, posts) {
if (err) {
resolve({})
} else {
resolve(posts)
}
});
})
}
Now I want to fetch all posts by $query variable
So in view I wrote like this
import React, { Component } from 'react';
import Relay from 'react-relay';
class BlogList extends Component {
constructor(props) {
super(props);
this.state = {
query: '',
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(){
this.props.relay.setVariables({query: this.state.query});
}
render() {
return (
<div className="input-group col-md-12">
<input type="text" onChange={this.handleChange.bind(this,"query")} value={this.state.query} name="query" placeholder="Enter Title or content"/><br/>
<span className="input-group-btn">
<button type="button" onClick={this.handleSubmit} className="btn btn-info btn-lg">
<i className="glyphicon glyphicon-search"></i>
</button>
</span>
</div>
)
}
};
export default Relay.createContainer(BlogList, {
initialVariables: {
query: ''
},
fragments: {
viewer: () => Relay.QL`
fragment on Setting {
id,
all_posts(first: 10000000,query: $query) {
edges {
node {
id,
_id,
title,
content,
createdAt,
username,
color,
images{
full
}
}
}
}
}
`,
},
});
And in routes I have
const SettingQueries = {
viewer: () => Relay.QL`query{
setting(currency: "USD")
}`,
}
export default [{
path: '/',
component: App,
queries: UserQueries,PostQueries,SettingQueries,
indexRoute: {
component: IndexBody,
},
childRoutes: [
,{
path: 'settings',
component: Setting,
queries: SettingQueries,
}]
}]
Things are working on /graphql as
but when I search from website it generates error in response
{
"data": {
"node": null
},
"errors": [
{
"message": "Abstract type Node must resolve to an Object type at runtime for field Root.node with value \"\",received \"null\".",
"locations": [
{
"line": 2,
"column": 3
}
]
}
]
}
as my web-browser is sending requests as below
Please suggest me what am I missing?
Also If I need to add some additional information please let me know.
The problem might be in your nodeDefinitions() function. First callback, also named idFetcher must return a single object. However, i see in your definition that you return a collection
var { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
var { type, id } = fromGlobalId(globalId);
...
}else if (type === 'Post') {
return getPosts(); // this should be getPost(id)
}
);
And thats why your next callback, known as typeResolver fails and returns you a null.
var { nodeInterface, nodeField } = nodeDefinitions(
...
(obj) => {
...
// here you get Promise/Collection instead of single Post instance, therefore condition failed
}else if (obj instanceof Post) {
return postType;
}
return null;
}
);
LordDave's answer revealed one problem in your code. As you commented in his answer, all_posts field of settingType was not working.
If you used mongoose library in your DB code, I see a problem with your query:
Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec({}, function(err, posts) {
if (err) {
resolve({})
} else {
resolve(posts)
}
});
Based on documentation of exec, change your query to
return Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec(function(err, posts) {
if (err) {
resolve({})
} else {
resolve(posts)
}
});
As exec returns a promise, you can even do
return Post.find({$or: [findTitle,findContent]}).sort({createdAt: 'descending'}).exec();
Finally I got it working by creating a new type 'postList' and defined it as below
var { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
var { type, id } = fromGlobalId(globalId);
if (type === 'User') {
return getUser(id);
}else if (type==='postList') {
return getpostList(id);
} else{
return null;
}
},
(obj) => {
if (obj instanceof User) {
return userType;
}else if (obj instanceof postList) {
return postListType;
}else{
return null;
}
}
);
In database.js
class postList {}
postList.id = "Post_id";
export {postList}
export function getpostList(id) {
return new postList
}
and under root fields as below
var postListType = new GraphQLObjectType({
name: 'postList',
description: 'List of posts',
  fields: () => ({
  id: globalIdField('postList'),
  posts: {
  type: postConnection,
  description: 'List of posts',
  args: {
...connectionArgs,
query: {type: GraphQLString}
},
  resolve: (_, args) => connectionFromPromisedArray(getAllPosts(_,args), args),
  },
}),
interfaces: [nodeInterface],
});
var Root = new GraphQLObjectType({
name: 'Root',
fields: () => ({
node: nodeField,
postList: {
type: postListType,
resolve:(rootValue)=> {
return getpostList()
}
},
})
});
I ran into this issue when I was using an InterfaceType and checked for the InterfaceType before the specialized ObjectType in the if-elseif-else of my TypeResolver

How to query property keys with conditions in LoopBack

How to query the key values of properties in LoopBack? In my case, the keys are array of objects.
The schema I generated is,
{
"bookingsLog": [
{
"checkIn": 1456079400000,
"checkOut": 1456165800000
},
{
"checkIn": 1456079400000,
"checkOut": 1456165800000
}
]
}
The remote method used to query is,
Resort.search = function(custom, cb) {}
Resort.remoteMethod('search', {
accepts: {
arg: 'custom',
type: 'object',
http: function(ctx) {
var _cIn = ctx.req.body.checkIn;
var _cOut = ctx.req.body.checkOut;
Resort.find({
where: {
and: [{ checkIn: { neq: _cIn } }, { checkOut: { neq: _cOut } },
{ checkIn: { neq: { between: [_cIn, _cOut] } } },
{ checkOut: { neq: { between: [_cIn, _cOut] } } }
]
}
}, function(err, resorts) {
console.log('Length is = ' + resorts.Length);
console.log('Res is = ' + JSON.stringify(resorts));
if(err){return ctx.res.send(err);}
if(resorts){return ctx.res.send(resorts);}
});
}
},
returns: {
arg: 'custom',
type: 'object'
}
});
Thanks in advance!

Sails-mongo. Find in array

I'm using sails.js and sails-mongo adapter. Suppose I have a model:
module.exports = {
attributes: {
shema: true
, attributes: {
description: {
type: 'TEXT'
, max: 200
}
, tags: {
type: 'ARRAY'
}
}
}
};
How can I carry out a search in an tags array?
Model.find({
'tags.title': {
contains: 'query'
}
})
.done(function (err, response) {
/**/
});
db.schools.find( { criteria },
{ atributes: { $elemMatch: { tags: value } } } )
there are a great example here: http://docs.mongodb.org/manual/reference/operator/projection/elemMatch/
with waterline
Model.native(function(err, collection) {
// Execute any query that works with the mongo js driver
collection.find( { criteria },
{ atributes: { $elemMatch: { tags: value } } } )
});

Resources