I have an object of data which I'd like to sort. I'm running the following object through Object.entries(data).map() to display it on my Front-End, but I'd like to maintain a specific order. Right now it seems that the order is somewhat random. I'm using .map to display a number of React components so sorting after the map doesn't seem to be possible.
const data = {
rent: {
value: '100'
},
},
legal: {
value: '300'
},
misc: {
value: '300'
},
horse: {
value: '400'
}
},
};
Ideally rent should be the first item when it's mapped out, and misc should be the last one. I've tried using .sort() but I don't seem to be having any luck.
What is the right way to achieve this? I assume some combination of Object.entries(data).sort().map() but I can't seem to figure it out.
You have to provide a compare function to your sort function, if wishing to use Object.entries:
const data = {
rent: {
value: '100'
},
legal: {
value: '300'
},
misc: {
value: '201'
},
horse: {
value: '400'
}
};
function compare(a, b){
if( a[1].value < b[1].value ){
return -1
} else if( a[1].value > b[1].value ){
return 1;
}
return 0;
}
console.log(Object.entries(data).sort(compare))
The code above becomes much cleaner if the original data structure is an array of objects:
const data = [
{ key: 'rent', value: '100' },
{ key: 'legal', value: '300' },
{ key: 'misc', value: '201' },
{ key: 'horse', value: '400' }
];
let sorted = data.sort(({key:k1, value:v1}, {key:k2, value:v2}) => {
if( v1 < v2 ){
return -1;
} else if( v1 > v2 ){
return 1;
}
return 0;
})
console.log('sorted arr', sorted)
the following will sort them so that rent is first misc is last and the rest are alphabetically
const data = {
rent: {
value: '100'
},
legal: {
value: '300'
},
misc: {
value: '300'
},
horse: {
value: '400'
}
};
function rent_ab_misc(obj=[]){
return Object.entries(obj).sort(([key1], [key2]) => {
if (key1 === 'misc' || key2==='rent') return 1;
if (key1 === 'rent' || key2 === 'misc') return -1;
return key1 > key2
})
}
// useage
const output = rent_ab_misc(data).map(([key,item])=>`<div>${key}:${item.value}</div>`)
console.log(output);
If you need to maintain a consistent order, use an array of objects instead of a single object. The property order of an object is not guaranteed.
const data = [
{ name: 'rent', value: '100' },
{ name: 'legal', value: '300' },
{ name: 'misc', value: '300' },
{ name: 'horse', value:'400' }
];
const sortedData = data.sort((a, b) => {
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
});
console.log(sortedData);
Related
I'm having the below list and I would like to add only these property names PRODUCT_TYPE, PRODUCT_TERM, PRODUCT_ID in myProduct. I want to ignore rest of the properties - I've around 100 properties and want to filter only a few of them from myProduct
Please find my code below:
const obj = {
myProduct: [
{
name: "PRODUCT_PRICE",
value: "234.324",
},
{
name: "PRODUCT_NAME",
value: "Insurance",
},
{
name: "PRODUCT_TYPE",
value: "Life",
},
{
name: "PRODUCT_TERM",
value: "Long",
},
{
name: "PRODUCT_ID",
value: "AP3232343JKD",
},
{
name: "PRODUCT_ENABLED",
value: "TRUE",
},
],
};
const allowedNames = [
'PRODUCT_TYPE',
'PRODUCT_TERM',
'PRODUCT_ID'
];
const updateCertainProperties = {
PRODUCT_ID: "app.productID",
PRODUCT_ENABLED: "app.product.enabled"
};
const productName = "testProduct_3234dfasfdk3msldf23";
const environment = obj.myProduct.map((o) => {
obj.myProduct.filter(product => allowedNames.includes(product.name));
if (updateCertainProperties[o.name]) o.name = updateCertainProperties[o.name];
if (o.name === "PRODUCT_NAME") o.value = productName;
return obj.myProduct;
});
console.log(obj.myProduct)
Expected output:
[
{ name: 'PRODUCT_NAME', value: 'testProduct_3234dfasfdk3msldf23' },
{ name: 'PRODUCT_TYPE', value: 'Life' },
{ name: 'PRODUCT_TERM', value: 'Long' },
{ name: 'app.productID', value: 'AP3232343JKD' },
{ name: 'app.product.enabled', value: 'TRUE' }
]
Can someone please help me how can I achieve this? Appreciated your help in advance!
You can create an array of allowed names and filter them out using includes()
css just for prettier output
UPDATE
added updateCertainProperties object values into allowedNames array and moved filter outside environment map.
const obj = {
myProduct: [
{
name: "PRODUCT_PRICE",
value: "234.324",
},
{
name: "PRODUCT_NAME",
value: "Insurance",
},
{
name: "PRODUCT_TYPE",
value: "Life",
},
{
name: "PRODUCT_TERM",
value: "Long",
},
{
name: "PRODUCT_ID",
value: "AP3232343JKD",
},
{
name: "PRODUCT_ENABLED",
value: "TRUE",
},
],
};
const allowedNames = [
'PRODUCT_TYPE',
'PRODUCT_TERM',
'PRODUCT_NAME'
];
const updateCertainProperties = {
PRODUCT_ID: "app.productID",
PRODUCT_ENABLED: "app.product.enabled"
};
allowedNames.push(...Object.values(updateCertainProperties));
const productName = "testProduct_3234dfasfdk3msldf23";
const environment = obj.myProduct.map((o) => {
if (updateCertainProperties[o.name]) o.name = updateCertainProperties[o.name];
if (o.name === "PRODUCT_NAME") o.value = productName;
return obj.myProduct;
});
obj.myProduct = obj.myProduct.filter(product => allowedNames.includes(product.name));
console.log(obj.myProduct)
.as-console-wrapper {
max-height: unset !important;
top: 0;
}
It sounds like you're describing filtering an array, not "excluding properties". You have an array of objects, with each object consisting of a name property and value property. And you only want objects with specific values in their name property.
Using .filter on the array, it might look something like this:
obj.myProduct = obj.myProduct.filter(p => (
p.name === 'PRODUCT_TYPE' ||
p.name === 'PRODUCT_TERM' ||
p.name === 'PRODUCT_ID'));
This would filter out all elements of the array which don't match the supplied condition.
This question already has answers here:
Sort array of objects by string property value
(57 answers)
Closed 2 years ago.
I have an array of objects:
array = [
{
name: 'fever',
possibility: '20%',
},
{
name: 'hiv',
possibility: '25%',
},
{
name: 'heart-attack',
possibility: '20%'
},
{
name: 'covid',
possibility: '40%',
},
]
I want to sort out the array of objects using its possibility. The object with higher possibility will be on top and if two or many objects have the same possibility then it will be sorted alphabetically. How can I do that?
Use String.localeCompare() with the numeric option to sort using possibility, but use - (minus) to get descending results. If the comparison returns 0 (equals), use localeCompare again to compare the names:
const array = [{"name":"fever","possibility":"20%"},{"name":"hiv","possibility":"25%"},{"name":"heart-attack","possibility":"20%"},{"name":"covid","possibility":"40%"}]
const result = array.sort((a, b) =>
-a.possibility.localeCompare(b.possibility, undefined, { numeric: true })
||
a.name.localeCompare(b.name)
)
console.log(result)
Maybe not only for this, but you could consider using the library Lodash.
It has a lot of cool features, such as sortBy that does exactly what you need.
_.sortBy(array, ['possibility']);
An additional note, is usually be better to store the possibility field (or any percentage value) with a number from 0 to 1, or at least from 1 to 100 as a number, and then adding the % char when displaying :)
To directly answer your questions, this works:
array.sort((e1, e2) => e1.possibility > e2.possibility ? 1 : -1)
Feel free to swap -1 and 1 to move from ascending to descending.
Try like this:
var array = [
{
name: 'fever',
possibility: '20',
},
{
name: 'hiv',
possibility: '25',
},
{
name: 'heart-attack',
possibility: '20'
},
{
name: 'covid',
possibility: '40',
},
]
function sortArray() {
return array.sort((a, b)=> b.possibility- a.possibility)
}
console.log(sortArray());
Prints:
[{
name: "covid",
possibility: "40"
}, {
name: "hiv",
possibility: "25"
}, {
name: "fever",
possibility: "20"
}, {
name: "heart-attack",
possibility: "20"
}]
Based on answer from here:
How to sort 2 dimensional array by column value?
Pure javascript solution would be :
array.sort((a,b) => a.possibility < b.possibility ? 1 : -1)
which would result in :
0: {name: "covid", possibility: "40%"}
1: {name: "hiv", possibility: "25%"}
2: {name: "heart-attack", possibility: "20%"}
3: {name: "fever", possibility: "20%"}
const array = [
{
name: 'fever',
possibility: '20%',
},
{
name: 'hiv',
possibility: '25%',
},
{
name: 'heart-attack',
possibility: '20%'
},
{
name: 'covid',
possibility: '40%',
},
];
const sorted = array.sort((a, b) => {
if (a.possibility > b.possibility) {
return 1;
}
if (a.possibility < b.possibility) {
return -1;
}
return 0;
});
console.log(sorted);
I've added another array entry just to double test the alphabetical ordering as a backup. Run the snippet and check the result:
let array = [
{
name: 'heart-attack',
possibility: '20%',
},
{
name: 'hiv',
possibility: '25%',
},
{
name: 'fever',
possibility: '20%'
},
{
name: 'covid',
possibility: '40%',
},
{
name: 'aaa',
possibility: '40%',
},
]
function compare( a, b ) {
newA = Number(a.possibility.slice(0, -1))
newB = Number(b.possibility.slice(0, -1))
if ( newA < newB ){
return -1;
} else if ( newA > newB ){
return 1;
} else if ( newA === newB && a.name < b.name){
return -1;
} else if ( newA === newB && a.name > b.name){
return 1;
}
return 0;
}
const sortedArr = array.sort(compare)
console.log( sortedArr )
I am looking for an efficient way of sorting the API response which is array of Objects. This Array has many fields and I just want to sort only few of them.
The Array looks like this
result = {type: Array(), status: Array(), nature: Array(), health: Array(), fitness: Array(), wealth: Array()}
and Array have name and value property like {name:"", value:""}
so let's say I just need to sort type, status, and nature out of this result. The thing that I have tried now looks like this which juts sorts one of the records.
const typeName = "type"
if(result[typeName]){
result[typeName] = sortingFunction(result[typeName], "name")
}
Now I need to sort other fields as well and also for few fields I need to sort on the basis of "value" property as well.
So please let me know if you have any efficient way of doing this.
You could create a sort function which can sort the given input object for the given keys.
I have create a sample function for sorting.
This function has two parameters.
First the object which needs to be sorted
Second option, you can pass the option for sort.
a. sortBy: Name of the property on which the function will perform the sort .
b. sortKeys: Array | String, the keys/key of the object which need to be sorted.
Function:
function sortObject(input, options = {}) {
if (!options)
return;
let keys = options.sortKeys;
let sortBy = options.sortby
if (!sortBy) {
console.error("sort by option is not defiend");
return;
}
if (!keys) {
console.error("sort keys are not defiend");
return;
}
if (Array.isArray(keys) && keys.length > 0) {
keys.forEach(item => sortObjectByKey(item, sortBy));
return;
}
if (typeof keys === "string" && keys) {
sortObjectByKey(keys, sortBy);
return;
}
function sortObjectByKey(sortKey, sortBy) {
input[sortKey].sort(function (a, b) {
let _a = (typeof a[sortBy] === "string") ? a[sortBy].toLowerCase() : a[sortBy];
let _b = (typeof b[sortBy] === "string") ? b[sortBy].toLowerCase() : b[sortBy];
if (_a < _b)
return -1
if (_a > _b)
return 1
return 0
});
}
}
Example:
//sortObject(sampleObject, { sortby: ["name", "value"], sortKeys: ["status", "type"] });
function sortObject(input, options = {}) {
if (!options)
return;
let keys = options.sortKeys;
let sortBy = options.sortby
if (!sortBy) {
console.error("sort by option is not defiend");
return;
}
if (!keys) {
console.error("sort keys are not defiend");
return;
}
if (Array.isArray(keys) && keys.length > 0) {
keys.forEach(item => sortObjectByKey(item, sortBy));
return;
}
if (typeof keys === "string" && keys) {
sortObjectByKey(keys, sortBy);
return;
}
function sortObjectByKey(sortKey, sortBy) {
input[sortKey].sort(function (a, b) {
let _a = (typeof a[sortBy] === "string") ? a[sortBy].toLowerCase() : a[sortBy];
let _b = (typeof b[sortBy] === "string") ? b[sortBy].toLowerCase() : b[sortBy];
if (_a < _b)
return -1
if (_a > _b)
return 1
return 0
});
}
}
let sampleObject = {
type: [
{ name: "c", value: 4 },
{ name: "a", value: 2 },
{ name: "b", value: 1 },
{ name: "d", value: 3 },
],
status: [
{ name: "c", value: 25 },
{ name: "a", value: 25 },
{ name: "b", value: 25 },
{ name: "d", value: 25 },
],
nature: [
{ name: "c", value: 25 },
{ name: "a", value: 25 },
{ name: "b", value: 25 },
{ name: "d", value: 25 },
],
}
sortObject(sampleObject, { sortby: "value", sortKeys: ["type"] });
sortObject(sampleObject, { sortby: "name", sortKeys: ["status", "nature"] });
console.log(sampleObject)
One way is to translate the object of arrays into an array of objects, then merge it back after sorting.
const result = {
type: ['foo', 'bar', 'baz'],
status: [4, 3, 5],
nature: ['forest', 'animal', 'water'],
health: ['athlete', 'couch potato', 'dead'],
fitness: [200, 50, 60],
wealth: [5, 2, 99]
};
// 1. combine
const combined = result.type.map((_, index) => {
return Object.fromEntries(Object.keys(result).map(key => [key, result[key][index]]));
});
// 2. example sort by status
combined.sort((a, b) => a.status - b.status)
// 3. merge
combined.forEach((object, index) => {
for (const [key, value] of Object.entries(object)) {
result[key][index] = value
}
})
console.log(result);
I have an array of objects, I need to delete a complete object based on the id
Input :
filters: [
{
key: "status",
label: "En attente",
value: "waiting",
id: 0
},
{
key: "dateDue[min]",
label: "15/12/2019",
value: "15/12/2019",
id: 1
},
{
key: "dateDue[max]",
label: "02/02/2020",
value: "02/02/2020",
id: 2
},
{
key: "bien",
values: [
{
label: "Studio Bordeaux",
value: 36,
id: 3
},
{
label: "Studio 2",
value: 34,
id: 184
}
]
},
{
key: "type",
values: [
{
type: "receipts",
label: "Loyer",
value: "loyer",
id: 4
},
{
type: "receipts",
label: "APL",
value: "apl",
id: 5
},
{
type: "spending",
label: "taxes",
value: "taxes",
id: 6
}
]
}
]
So I created a removeItem method with the id that must be deleted in parameters
removeItem method :
removeItem = (e, id) => {
const { filters } = this.state;
const remove = _.reject(filters, el => {
if (!_.isEmpty(el.values)) {
return el.values.find(o => o.id === id);
}
if (_.isEmpty(el.values)) {
return el.id === id;
}
});
this.setState({
filters: remove
});
};
I use lodash to make my job easier and more specifically _.reject
My issue is the following :
I manage to correctly delete the classic objects for example
{
key: "status",
label: "En attente",
value: "waiting",
id: 0
}
but my method however does not work for objects of the following form
{
key: "bien",
values: [
{
label: "Studio Bordeaux",
value: 36,
id: 3
},
{
label: "Studio 2",
value: 34,
id: 184
}
]
},
currently the whole object is deleted and not only the object in the values array according to its id
Here is my codesandbox!
thank you in advance for your help
EDIT
I found a solution with lodash (compact), I share my solution here :
removeIdFromCollection = id => {
const { filters } = this.state;
const newFilters = [];
_.map(filters, filter => {
if (filter.values) {
const valuesTmp = _.compact(
_.map(filter.values, value => {
if (value.id !== id) return value;
})
);
if (!_.isEmpty(valuesTmp)) {
return newFilters.push({
key: filter.key,
values: valuesTmp
});
}
}
if (filter.id && filter.id !== id) return newFilters.push(filter);
});
return newFilters;
};
removeItem = id => e =>
this.setState({
filters: this.removeIdFromCollection(id)
});
The values false, null, 0, "", undefined, and NaN are removed with lodash compact (_.compact(array))
Here is my updated codesandbox
You will need to filter the filters array and each values separately. Below is a recursive function which will remove items with the given id from the filters array and from the values property.
PS. This example is not using Lodash as I think it is not needed in this case.
removeIdFromCollection = (collection, id) => {
return collection.filter(datum => {
if (Array.isArray(datum.values)) {
datum.values = this.removeIdFromCollection(datum.values, id);
}
return datum.id !== id;
});
}
removeItem = (e, id) => {
const { filters } = this.state;
this.setState({
filters: this.removeIdFromCollection(filters, id),
});
};
The problem would be the structure of the object. You'll need to refactor for that inconvenient array out of nowhere for uniformity:
// Example
filters: [
...
{
key: "type",
values: [
{
type: "receipts",
label: "Loyer",
value: "loyer",
id: 4
},
...
]
...
}
// could be
filters: [
...
{
key: "type-receipts",
label: "Loyer",
value: "loyer",
id: 4
}
...
]
Repeat the pattern on all of it so you could just use the native array filter like this:
const newFilters = filters.filter(v => v.id !== id);
this.setState({
filters: newFilters,
});
I found a solution with lodash, I share it with you here :
removeIdFromCollection = id => {
const { filters } = this.state;
const newFilters = [];
_.map(filters, filter => {
if (filter.values) {
const valuesTmp = _.compact(
_.map(filter.values, value => {
if (value.id !== id) return value;
})
);
if (!_.isEmpty(valuesTmp)) {
return newFilters.push({
key: filter.key,
values: valuesTmp
});
}
}
if (filter.id && filter.id !== id) return newFilters.push(filter);
});
return newFilters;
};
removeItem = id => e =>
this.setState({
filters: this.removeIdFromCollection(id)
});
Here is my updated codesandbox
Using Array.reduce, I am trying to count the elements that have the same value for a specific property. I want to put the result in an array of objects containing a property for the value of the grouped by property and another one for the count. How can I do this easily in javascript ?
const CATEGORY = {
STRATEGY: 'STRATEGY',
CONTENT: 'CONTENT',
ADVERTISING: 'ADVERTISING',
MEASURMENT: 'MEASURMENT'
}
const lessons = [
{
title: 'ohoho',
category: CATEGORY.STRATEGY
}, {
title: 'hihihi',
category: CATEGORY.CONTENT
}, {
title: 'hello',
category: CATEGORY.CONTENT
}
]
let categoryLessonCount = lessons.reduce(function (acc, lesson) {
acc[lesson.category] ? acc[lesson.category]++ : acc[lesson.category] = 1
return acc
}, {})
console.log(categoryLessonCount[CATEGORY.STRATEGY])
console.log(categoryLessonCount[CATEGORY.CONTENT])
Actual categoryLessonCount value :
Object
{
STRATEGY: 1,
CONTENT: 2
}
Wanted categoryLessonCount value :
Array
[
{
title: 'STRATEGY',
count: 1
}, {
title: 'CONTENT',
count: 2
}
]
You already got the what you want just transform it into an array
const CATEGORY = {
STRATEGY: 'STRATEGY',
CONTENT: 'CONTENT',
ADVERTISING: 'ADVERTISING',
MEASURMENT: 'MEASURMENT'
}
const lessons = [{
title: 'ohoho',
category: CATEGORY.STRATEGY
}, {
title: 'hihihi',
category: CATEGORY.CONTENT
}, {
title: 'hello',
category: CATEGORY.CONTENT
}]
let count = lessons.reduce(function(acc, lesson) {
acc[lesson.category] ? acc[lesson.category] ++ : acc[lesson.category] = 1
return acc
}, {})
// transform count into what you want
let categoryLessonCount = [];
for (let cat in count) {
categoryLessonCount.push({
'title': cat,
'count': count[cat]
});
}
console.log(categoryLessonCount)
Something like this should work:
let categoryLessonCount = lessons.reduce(function(acc, lesson) {
let found = false
for (const item of acc) {
if (item.title === lesson.category) {
item.count++
found = true
}
}
if (!found) {
acc.push({
title: lesson.category,
count: 1
})
}
return acc
}, [])
Your main issue is that your accumulating an object but expecting an array (note the final argument to reduce).
Short solution using Object.keys and Array.prototype.map functions:
...
let categoryLessonCount = lessons.reduce(function (acc, lesson) {
acc[lesson.category] ? acc[lesson.category]++ : acc[lesson.category] = 1
return acc
}, {})
let counts = Object.keys(categoryLessonCount).map(
(k) => ({title: k, count: categoryLessonCount[k]})
)
console.log(counts);