React: Array sort not reading state value? - reactjs

So im working on a pretty basic CRUD app to strengthen my react skills. Right now it's a table of sensors with a state that looks like this:
this.state = {
sensors: [
{
id: "1",
name: "Temp001DEV",
device: "Temp",
temp: "56.4",
humidity: "33.1",
active: true
},
{
id: "2",
name: "Humidity002DEV",
device: "Humidity",
temp: "",
humidity: "45.6",
active: true
}
],
filterName: "",
sortBy: "name"
};
}
(Few demo sensors tossed in there for some fake data).
I also have a sorting function (It's a WIP, my javascript is rusty). That looks like so:
filterSort = () => {
//Check for filter, then sort
if (this.state.filterName !== "") {
const filteredSensors = this.state.sensors.filter(sensor =>
sensor.name.toLowerCase().includes(this.state.filterName.toLowerCase())
);
console.log(filteredSensors);
return filteredSensors.sort(function(a, b) {
if(a.name < b.name) {
return -1;
}
if(a.name > b.name) {
return 1;
}
else {
return 0;
}
});
}
//If no filter exists, just sort
else {
return this.state.sensors.sort(function(a, b) {
if(a.name < b.name) {
return -1;
}
if(a.name > b.name) {
return 1;
}
else {
return 0;
}
});;
}
};
The table is created by mapping through the returned "Filtered/Sorted" array and the table is created that way (Which works). I also have dropdowns and a text input that update the sortBy and filterName state (Which also works).
Where im running into issues is where i actually call sort on the filteredSensors. Right now I have a.name < b.name etc... (Which does work). But I want it to actually sort based on the state value sortBy (Which can be either name, active, or device based on a dropdown I have). However when I try to do something like const sortVal = this.state.sortBy and then swap out .name for .sortVal it doesn't work.
It will just say that the sortVal is never used. Any ideas on how to swap out the sorting based off a state value?
Thanks!

You're trying to use a variable called "sortVal" on the elements on the sort function by using dot notation. If you instead use a[sortVal] and b[sortVal] it will work as it will instead use the variable that sortVal evaluates to (such as humidity) as intented.

Related

React : Pushing result of map() to an array

Hello I am trying to map through an array of objects and push them to a new array.
My ISSUE : only the last item of the object is being pushed to the new array
I believe this has to do with React life cycle methods but I don't know where I should I loop and push the values to the array to get the full list
//My object in an array named states
var states = [{"_id":"Virginia","name":"Virginia","abbreviation":"VN","__v":0},{"_id":"North Carolina","name":"North Carolina","abbreviation":"NC","__v":0},{"_id":"California","name":"California","abbreviation":"CA","__v":0}];
export function StateSelect()
{
**EDIT 1**
const options = [];
function getStates()
{
//This is how I am looping through it and adding to an array
{ states.length > 0 &&
states.map(item =>
(
console.log(`ITEM: ${JSON.stringify(item)}`),
options.push([{ value: `${item.name}`, label: `${item.name}`}])
))
}
}
return( {getStates()}: );
}
Thank you
It looks like your getStates() might not even be returning anything... but assuming it is, I believe you should be able to accomplish this using a forEach() fn in order to push values into your options array... Try adding the following into your map:
states.map((item) => {
console.log(`ITEM: ${JSON.stringify(item)}`);
let processed = 0;
item.forEach((i) => {
options.push([{ value: `${i.name}`, label: `${i.name}`}]);
processed++;
if(processed === item.length) {
// callback fn, or return
}
}
.map usually used to return another result, you could just use .forEach
In fact, you don't really need to declare options at all, just use .map on state to return the result would be fine.
return states.length > 0 && states.map(({ name }) => {
return { value: name, label: name };
});

How to write array with deleted image id?

When i remove images from news i catch id, and id come to along.
How to write in array all this lonlies id ?
How to create streamIds array with streamId ?
this.state = {
mainImage: null,
mainImageUrl: "",
crop: {
aspect: 2 / 1
},
pixelCrop: null,
cropped: false,
loaded: false,
streamIds: []
};
removeImage(imageKey, streamId) {
const {singleNews} = this.props;
let streamIds = this.state.streamIds;
console.log(streamId);
singleNews.secondaryImages.splice(imageKey, 1);
if (!singleNews.secondaryImages.length) {
singleNews.secondaryImages = null;
delete singleNews.secondaryImages;
this.props.updateSingleNews(null, singleNews);
} else {
streamIds.push(streamId);
singleNews.secondaryImages.map(image => {
const index = singleNews.secondaryImages.indexOf(image);
if (index > -1) {
singleNews.secondaryImages.slice(index, 1);
FilesApi.getDocument(image.streamId).then(resp => {
singleNews.secondaryImages[index] = new File([resp], image.name, {lastModified: Date.now()});
});
}
});
this.props.updateSingleNews('streamIds', streamIds);
}
}
this is your method
If not in this func where i need to place
if you want to keep the array of ids in the same component, use
let streamIds = [];
at the top of your react component and do
removeImage (imageKey, streamId) {
console.log(streamId);
streamIds.push(streamId); // insert the item to array
}
in your removeImage method
if you want to keep the removed ids in the application state, then the concept is the same, but it need to be done on the state management tool you are using (like redux, mobx etc)

Could not evaluate cost function using sumBy function in Lodash

I have a function called cost in scope.The Primary task of this function is to fetch arrays, filter it based on the requirement and evaluate the cost of the task. It has two parameters called tasks and time.While debugging the function, the array of data passes on tasks and it retains in the second line where my loadash sumBy function exists. I could not get cost out of it.
$scope.cost = function(tasks, time) {
return _.sumBy(tasks, function(t) {
if (t.billable && t[time]) {
var types = _.filter($scope.taskTypes, function(typ) {
return (t.type == typ._id);
});
return t[time] * types[0].rate;
} else {
return 0;
}
});
};
My array has these values.
{
billable: true,
estimatedTime: 6,
jobId: 1234,
label: gh,
status: ÄSSIGNED,
type: 3D-Modelling,
_id:08746574687
}

Meteor return length of array in mongodb

In my users profile collection I have array with image objects in it.
A user can have a max of 3 images in their profile collection. If the user has 3, throw an error that the maximum has been reached. The user has the option to remove an image themselves in the frontend.
I thought the solution would be to check the length of the array with $size. if it's less then 3, insert the image, else throw error.
I'm using the tomi:upload-jquery package.
client:
Template.uploadImage.helpers({
uploadUserData: function() {
return Meteor.user();
},
finishUpload: function() {
return {
finished: function(index, fileInfo, context) {
Meteor.call('insert.profileImage', fileInfo, function(error, userId) {
if (error) {
// todo: display modal with error
return console.log(error.reason);
} else {
// console.log('success ' +userId);
// console.log('success ' + fileInfo);
}
});
}
};
}
});
The method (server) I use:
'insert.profileImage': function(postImage) {
check(postImage, Object);
// check array profile.images max 3
Meteor.users.update(this.userId, {
$push: {
'profile.images': postImage
}
});
},
You may do it with a function using the $where operator:
'insert.profileImage': function(postImage) {
var updateResults;
check(postImage, Object);
updateResults = Meteor.users.update(
{
_id : this.userId,
$where : 'this.profile.images.length < 3' //'this' is the tested doc
},
{
$push: {
'profile.images': postImage
}
});
if(updateResults === 0) {
throw new Meteor.Error('too-many-profile-images',
'A user can only have up to 3 images on his/her profile');
}
},
The Mongo docs warns about potential performance issues (if you run a JavaScript function on all documents of the store, you're in for bad surprises) but since we also search by _id I guess it should be fine.
This way, the update just doesn't run if the user has too many images. You can also check the number of affected document (the return value of the update) to know if something happened. If nothing (returns 0) happened, there's not many possibilities: The user has too many images.
Use the $exists operator to check the existence of all documents that have at least a fourth profile image array element (index position 3) with the dot notation. For example you could use it to check whether the size of the profile.image array is greater than 3 with the find() method as follows:
var hasSizeGreaterThanThree = Meteor.users.find(
{
'_id': this.userId,
'profile.image.3': { '$exists': true }
}).count() > 0;
So you could use that in your code as:
'insert.profileImage': function(postImage) {
check(postImage, Object);
// check array profile.images max 3
var hasSizeGreaterThanThree = Meteor.users.find(
{
'_id': this.userId,
'profile.image.3': { '$exists': true }
}).count() > 0;
if (!hasSizeGreaterThanThree){
Meteor.users.update(this.userId, {
$push: {
'profile.images': postImage
}
});
}
},

Query for most favorited in each category

I have a table of creations. Each belongs to a category (with categoryId). They also have a field called statFavorites.
I want to return a flat list of the single creation with the most favorites for each category in a list.
The only way I can think of doing it is with groupedMapReduce. Is there another way?
var categories; // objects with an id
r.table('creations')
.filter(function(creation) {
return categories.filter(function(category) {
return category.id == creation.categoryId
}).length > 0
})
.groupedMapReduce(function(row) {
return row("categoryId")
}
, function(row) { return row }
, function(best, creation) {
return r.branch(creation("statFavorites").gt(best("statFavorites")), creation, best
})
Two things happening above: First, I'm filtering the creations to only match the categories I care about (equivalent of an in query in mongo. How to do this in rethink?)
Second, I'm getting the most favorited of each one.
Is there a better way of doing this? It may also be ok to pre-calculate things when I'm writing data.
You can do something like this:
The equivalent of in is contains
http://www.rethinkdb.com/api/javascript/contains/
categories = [id1, id2, id3]
r.table('creations')
.filter(function(creation) {
return r.expr(categories).contains(creation("id"))
})
.groupedMapReduce(function(row) {
return row("categoryId")
}
, function(row) { return row }
, function(best, creation) {
return r.branch(creation("statFavorites").gt(best("statFavorites")), creation, best
})
If categories is an object like
{
id1: true,
id2: true
}
You can also use hasFields instead of contains
http://www.rethinkdb.com/api/javascript/has_fields/
r.table('creations')
.filter(function(creation) {
return r.expr(categories).hasFields(creation("id"))
})
.groupedMapReduce(function(row) {
return row("categoryId")
}
, function(row) { return row }
, function(best, creation) {
return r.branch(creation("statFavorites").gt(best("statFavorites")), creation, best
})

Resources