How do I sort in the front end with javascript an array of items based on other fields inside the item [duplicate] - javascript

This question already has answers here:
How to sort an array of objects by multiple fields?
(38 answers)
Closed 2 years ago.
I am trying to create 2 sort types in my FE with javascript for my items that come from the backend but I don't know what the logic should look like for it. Where can I read about something like this?
The first way would be to sort them by the date (The most recent first)
The second way would be to sort them by the total number of the awards (Highest number first)
This is how my data looks like:
[
{
awards: {awardOne: 1, awardTwo: 4, awardThree: 8}
createdAt: "2020-11-13T21:12:50.742Z"
text: "Some text"
username: "username"
},
{
awards: {awardOne: 1, awardTwo: 4, awardThree: 8}
createdAt: "2020-11-13T21:12:50.742Z"
text: "Some text"
username: "username"
},
{
awards: {awardOne: 2, awardTwo: 3, awardThree: 2}
createdAt: "2020-11-13T21:12:50.742Z"
text: "Some text"
username: "username"
},
]

Here is an example sorting the data by number of awards in each object: code sandbox. I'm reducing the items in each awards object to a single value and comparing those.
To sort by date, you can use localeCompare like others have pointed out and use a similar pattern.
Update: I just added an working example of sorting by date to the same sandbox

I am thinking you can sort the items by date lexiographically.
Using String.prototype.localeCompare your code would something like
data.sort((a, b) => {
return ('' + a.createdAt).localeCompare(b.createdAt);
}
Source
To sort by number of awards you would need a smaller function, that calculates number of awards, and then write something like:
data.sort((a, b) => {
return (calcNumOfAwards(a) - calcNumOfAwards(b))
}

Related

How to get second duplicate value rather than first value from JSON Array in React JS using lodash? [duplicate]

This question already has an answer here:
Lodash uniqBy update the latest value
(1 answer)
Closed 9 months ago.
I am working on one project where I need to remove duplicate values from JSON array object with some specification in react JS. I have tried to remove using _.uniqBy but in the output it took very first value from duplicate value which is I don't want.
Suppose You have an array JSON like:
[ { id: 1, name: 'bob' }, { id: 2, name: 'bill' }, { id: 1, name: 'alice' } ]
using _.uniqBy I got [ { id: 1, name: 'bob' }, { id: 2, name: 'bill' }] this output.
but I want [ { id: 2, name: 'bill' }, { id: 1, name: 'alice' } ] this output.
As you can see I want output whose name is alice not bob along with id:1.
Can anyone help me?
Thank you.
My first thought is to use a reduce, and shove the items in a map, then get the values:
Object.values(items.reduce((map, item) => ({ ...map, [item.id]: item }), {}))
This is probably not very efficient though if you're dealing with large arrays of have performance concerns.
It's a quick and dirty one-liner. If you want something more efficient I'd take a look at the lodash source code and tweak it to your needs or write something similar:
https://github.com/lodash/lodash/blob/2f79053d7bc7c9c9561a30dda202b3dcd2b72b90/.internal/baseUniq.js

Javascript object as a type of mini database?

Is it possible to use a JavaScript object as a type of mini database? I often find myself needing a kind of database structure when I'm coding in JS but it feels like overkill to use an actual database like MySQL (or similar).
As an example, let's say I need to structure this data as a JS object:
Object idea: Stuff to sell
Items to sell: The junk in the garage
Object structure: List all items including item name, item condition, and item value
In order to make this into a JS object I would maybe write:
var stuffToSell = {};
Then my first item would maybe look like:
var stuffToSell = {
item : "Coffee Maker",
condition : "Good",
price : 5
};
Now to me this seems like I'm on the right track, until I come to add another item and I end up having to use the properties item, condition, and price again in the same JS object — which feels wrong? — or is it?? At this point my brain keeps shouting the word "ARRAY!" at me but I just can't see how I can use an array inside the object, or an object inside an array to achieve what I want.
My end goal (in this simplified example) is to be able to then use object-oriented syntax to be able to access certain items and find out specific information about the item such as price, condition etc. For example if I want to know the price of the "coffee maker" I would like to write something like:
stuffToSell["coffee maker"].price
...and then the result above should be 5.
I feel like I'm on the right track but I think I'm missing the array part? Could someone please tell me what I'm missing or maybe what I'm doing completely wrong! And also if it is wrong to have duplicate property names in the same JS object? For example, is it okay to have:
var stuffToSell = {
item : "Coffee Maker",
price : 5,
item : "Mountain Bike",
price : 10,
item : "26 inch TV",
price : 15
};
...it seems wrong because then how does JS know which price goes with which item??
Thanks in advance :)
You're definitely on the right track!
A lot of people will refer to what you're talking about as a hash.
Here's my suggested structure for you:
var store = {
coffee_maker: {
id: 'coffee_maker',
description: "The last coffee maker you'll ever need!",
price: 5,
},
mountain_bike: {
id: 'mountain_bike',
description: 'The fastest mountain bike around!',
price: 10,
},
tv: {
id: 'tv',
description: 'A big 26 inch TV',
price: 15,
},
}
Having a structure like that will let you do this:
store.mountain_bike.price // gives me 10
Need an array instead, say for filtering or looping over?
Object.keys gives you an Array of all the object's keys in the store ['coffee_maker', 'mountain_bike', 'tv']
// Now we just have an array of objects
// [{id: 'coffee_maker', price: 5}, {id: 'mountain_bike', price: 10} ...etc]
var arr = Object.keys(store).map(el => store[el])
Need to just filter for items that are less than 10?
This will give us an array of products less than 10:
// gives us [{id: 'coffee_maker', price: 5}]
var productsUnder10 = arr.filter(el => el.price < 10)
These techniques can be chained:
var productsOver10 = Object.keys(store)
.map(el => store[el])
.filter(el => el.price > 10)
Need to add a product?
store['new_product'] = {
id: 'new_product',
description: 'The Newest Product',
price: 9000,
}
Here's another way, which would be good to start getting used to.
This is a 'safe' way to update the store, read up on immutability in javascript to learn about it
store = Object.assign({}, store, {
'new_product': {
id: 'new_product',
description: 'The Newest Product',
price: 9000,
}
})
...and another way, that you should also read up on and start using:
This is the object spread operator, basically just an easier way to work with immutable structures
store = {
...store,
'new_product': {
id: 'new_product',
description: 'The Newest Product',
price: 9000,
}
}
Resources
JavaScript Arrow Functions
Object and Array Spread Syntax
Immutable Javascript using ES6 and beyond
You can actually use json or create an array of objects.If using a separate file to store the objects, first load the file. Use array filter method to get an new array which matches the filter condition , like you want to get the item with id 1. This will return an array of objects.
var dict = [{
'id': 1,
'name': 'coffee-mug',
'price': 60
},
{
'id': 2,
'name': 'pen',
'price': 2
}
]
function getItemPrice(itemId) {
var getItem = dict.filter(function(item) {
return item.id === itemId
});
return getItem[0].price;
}
console.log(getItemPrice(1))
JSON objects don't support repeated keys, so you need to set unique keys.
Put an id as your key to group your items:
var stuffToSell = {
'1': {
item: "Coffee Maker",
price: 5
},
'2': {
item: "Mountain Bike",
price: 10
}
.
.
.
}
Now you can access the item's price very fast.
Look at this code snippet (Known Ids)
var stuffToSell = {
'1': {
item: "Coffee Maker",
price: 5
},
'2': {
item: "Mountain Bike",
price: 10
},
'3': {
item: "26 inch TV",
price: 15
}
};
let getPrice = (id) => stuffToSell[id].price;
console.log(getPrice('1'));
See? the access to your items it's fast and your code follows a readable structure.
Look at this code snippet (Item's name as key)
var stuffToSell = {
'Coffee Maker': {
price: 5
},
'Mountain Bike': {
price: 10
},
'26 inch TV': {
price: 15
}
};
let getPrice = (id) => stuffToSell[id].price;
console.log(getPrice('Coffee Maker'));
Look at this code snippet (Item's name: price)
var stuffToSell = {
'Coffee Maker': 5,
'Mountain Bike': 10,
'26 inch TV': 15
};
let getPrice = (id) => stuffToSell[id];
console.log(getPrice('Coffee Maker'));

pass along the field and value while aggregating

I had an output document from a pipeline something like : {upvotes : 1, downvotes : 5}. I just wanted to pass that along to the next part of the pipeline. I did something like this:
{
$group :{
_id : "$_id",
upvotes : {$max : "$upvotes"},
downvotes : {$max : "$downvotes"},
average : {$avg : "$statements.result"},
count : {$max : "$count"}
}
}
I didn't want to use max I don't want the maximum of anything. I just wanted the field and value: {upvotes : 1, downvotes : 5}. lets say they were not numbers. lets say the fields were object I want all the objects for that field to be outputted not the max.
make this question clearer: how can I get the the field and value without using max?
outputs:
[ { _id: 57aea6791016c0b023a71e9d,
upvotes: 3,
downvotes: 1,
average: 7.857142857142857,
count: 5 },
{ _id: 57aed883139c1e702beb471e,
upvotes: 0,
downvotes: 1,
average: 7,
count: 1 } ]
The output is good but I don't want to use max to get it.
For some reason I think I need an Accumulator Operator. That is why I use max.
Since you are grouping by "$_id", you're actually not grouping at all. Your results are going to correlate 1:1 to your initial data set. To accomplish what you are asking, passing along upvotes and downvotes without using an aggregate function like $max, you'd just include them in the _id for your group stage which is where you specify your group by key.
So you would do:
{
$group :{
_id : {
"_id":"$_id",
"upvotes": "$upvotes",
"downvotes": "$downvotes"
}
average : {$avg : "$statements.result"},
count : {$max : "$count"}
}
}
But still, you are going to end up with a 1:1 correlation to your original data.
Following your question thread, your pipeline in question is most probably a preceding pipeline step from an $unwind operation thence the following suggested solution is based on that assumption. Please feel free to suggest otherwise.
To include a field in the $group pipeline using the accumulators, the $first or $last seem most appropriate here since you have just flattened an array and those operators will return a value from the first/last document for each group. What is means is if the field is not part of a flattened array then $first/$last would give you its actual value before the $unwind took place.
{
"$group" :{
"_id": "$_id",
"upvotes": { "$first": "$upvotes"},
"downvotes": { "$first": "$downvotes"},
"average": { "$avg": "$statements.result"},
"count": { "$max": "$count"}
}
}

Mongoose: Sorting

what's the best way to sort the following documents in a collection:
{"topic":"11.Topic","text":"a.Text"}
{"topic":"2.Topic","text":"a.Text"}
{"topic":"1.Topic","text":"a.Text"}
I am using the following
find.(topic:req.body.topic).(sort({topic:1}))
but is not working (because the fields are strings and not numbers so I get):
{"topic":"1.Topic","text":"a.Text"},
{"topic":"11.Topic","text":"a.Text"},
{"topic":"2.Topic","text":"a.Text"}
but i'd like to get:
{"topic":"1.Topic","text":"a.Text"},
{"topic":"2.Topic","text":"a.Text"},
{"topic":"11.Topic","text":"a.Text"}
I read another post here that this will require complex sorting which mongoose doesn't have. So perhaps there is no real solution with this architecture?
Your help is greatly appreciated
i will suggest you make your topic filed as type : Number, and create another field topic_text.
Your Schema would look like:
var documentSchema = new mongoose.Schema({
topic : Number,
topic_text : String,
text : String
});
Normal document would look something like this:
{document1:[{"topic":11,"topic_text" : "Topic" ,"text":"a.Text"},
{"topic":2,"topic_text" : "Topic","text":"a.Text"},
{"topic":1,"topic_text" : "Topic","text":"a.Text"}]}
Thus, you will be able to use .sort({topic : 1}) ,and get the result you want.
while using topic value, append topic_text to it.
find(topic:req.body.topic).sort({topic:1}).exec(function(err,result)
{
var topic = result[0].topic + result[0].topic_text;//use index i to extract the value from result array.
})
If you do not want (or maybe do not even can) change the shape of your documents to include a numeric field for the topic number then you can achieve your desired sorting with the aggregation framework.
The following pipeline essentially splits the topic strings like '11.Topic' by the dot '.' and then prefixes the first part of the resulting array with a fixed number of leading zeros so that sorting by those strings will result in 'emulated' numeric sorting.
Note however that this pipeline uses $split and $strLenBytes operators which are pretty new so you may have to update your mongoDB instance - I used version 3.3.10.
db.getCollection('yourCollection').aggregate([
{
$project: {
topic: 1,
text: 1,
tmp: {
$let: {
vars: {
numStr: { $arrayElemAt: [{ $split: ["$topic", "."] }, 0] }
},
in: {
topicNumStr: "$$numStr",
topicNumStrLen: { $strLenBytes: "$$numStr" }
}
}
}
}
},
{
$project: {
topic: 1,
text: 1,
topicNumber: { $substr: [{ $concat: ["_0000", "$tmp.topicNumStr"] }, "$tmp.topicNumStrLen", 5] },
}
},
{
$sort: { topicNumber: 1 }
},
{
$project: {
topic: 1,
text: 1
}
}
])

How to make a MongoDB query sort on strings with -number postfix?

I have a query:
ownUnnamedPages = Entries.find( { author : this.userId, title : {$regex: /^unnamed-/ }}, {sort: { title: 1 }}).fetch()
That returns the following array sorted:
[ {
title: 'unnamed-1',
text: '<p>sdaasdasdasd</p>',
tags: [],
_id: 'Wkxxpapm8bbiq59ig',
author: 'AHSwfYgeGmur9oHzu',
visibility: 'public' },
{
title: 'unnamed-10',
text: '',
author: 'AHSwfYgeGmur9oHzu',
visibility: 'public',
_id: 'aDSN2XFjQPh9HPu4c' },
{
title: 'unnamed-2',
text: '<p>kkhjk</p>',
tags: [],
_id: 'iM9FMCsyzehQvYGKj',
author: 'AHSwfYgeGmur9oHzu',
visibility: 'public' },
{
title: 'unnamed-3',
text: '',
tags: [],
_id: 'zK2w9MEQGnwsm3Cqh',
author: 'AHSwfYgeGmur9oHzu',
visibility: 'public' }]
The problem is that it seems to sort on the first numeric character so it thinks the proper sequence is 1, 10, 2, 3, etc....
what I really want is for it to sort on both the whole numerical part so that 10 would be at the end.
I'd prefer not to do this by having additional numbers such as 01 or 001 for the numbers.
How would I do that?
You can use
db.collectionName.find().sort({title: 1}).collation({locale: "en_US", numericOrdering: true})
numericOrdering flag is boolean and is Optional. Flag that determines whether to compare numeric strings as numbers or as strings.
If true, compare as numbers; i.e. "10" is greater than "2".
If false, compare as strings; i.e. "10" is less than "2".
Default is false.
See mongo's collation documentation for an updated explanation of those fields.
MongoDB can't sort by numbers stored as strings. You either have to store the number as an integer in its own field, pad with leading zeroes, or sort the results after they've been returned from the database.
If you 0 pad the numbers you will be able to search as a string in the right order, so instead of 0,1,2,3,4,5,6,7,8,9,10,11...
use 01,02,03,04,05,06,07,08,09,10,11...
and a string search will return them in order.
The mongo documentation said you can use Collation for this goal
as #Eugene Kaurov said you can use
.collation({locale: "en_US", numericOrdering: true})
this is the official documentation:
mongo ref
and be aware that the accepted answer is not correct now
In mongo is not possible (sort strings in ascii) but you can sort with the below function after you get all documents from the collection
const sortString = (a, b) => {
const AA = a.title.split('-');
const BB = b.title.split('-');
if (parseInt(AA[1], 10) === parseInt(BB[1], 10)) {
return 0;
}
return (parseInt(AA[1], 10) < parseInt(BB[1], 10)) ? -1 : 1;
};
document.sort(sortString);
In my case we work with aggregations. The approach was to sort using the length of our string; only works when the text part is always the same (unnamed- in your case)
db.YourCollection.aggregate([
{
$addFields: {
"TitleSize": { $strLenCP: "$Title" }
}
},
{
$sort: {
"TitleIdSize": 1,
"Title": 1
}
}
]);
Now we sort using length, the second sort will use the content.
Example:
"unnamed-2", Titlesize: 9
"unnamed-7", Titlesize: 9
"unnamed-30", Titlesize: 10
"unnamed-1", Titlesize: 9
The first sort will put the ids in this order: 2, 7, 1, 30. Then the second sort will put the ids in the correct order: 1, 2, 7, 30.

Categories

Resources