My res.data always randomly sorted, how to sort res.data by _id ?
const [income, setIncome] = useState([]);
const [perc, setPerc] = useState(0);
useEffect(() => {
const getIncome = async () => {
try {
const res = await userRequest.get("orders/income");
setIncome(res.data);
setPerc((res.data[1].total * 100) / res.data[0].total - 100);
console.log(res.data);
} catch {}
};
getIncome();
}, []);
console.dev:
0: {_id: 10, total: 990}
1: {_id: 11, total: 20}
2: {_id: 6, total: 448}
3: {_id: 9, total: 700}
4: {_id: 8, total: 100}
5: {_id: 7, total: 900}
You can do this to sort based on it:
res.data.sort((x, y) => x._id - y._id)
This will sort in ascending order based on the _id attribute for each item of the res.data array.
You can use Array.sort for that:
const data = [
{_id: 10, total: 990},
{_id: 11, total: 20},
{_id: 6, total: 448},
{_id: 9, total: 700},
{_id: 8, total: 100},
{_id: 7, total: 900}
]
const sorted = data.sort((a, b) => a._id - b._id)
console.log(data)
In this example I use subtraction "hack" do determinate order. It works only for numbers, if you want to compare strings, for example, you can pass custom compare function (compareFunction(a, b)) that returns:
> 0 - sort a after b
< 0 - sort a before b
=== 0 - keep original order of a and b
You can use the built-in sort function like this:
myArray = [{_id: 10, total: 990},
{_id: 11, total: 20},
{_id: 6, total: 448},
{_id: 9, total: 700},
{_id: 8, total: 100},
{_id: 7, total: 900}];
// now myArray is sorted "in place" in a descending order.
myArray.sort((a, b) => a._id - b._id);
// you can flip it so it is ordered in an ascending order:
myArray.sort((a, b) => b._id - a._id);
Notice that sorting by id is not recommended because there is no business logic related to the order of ids. Why should item 1 be shown before item 2?
The order should depend on a business-related field, for example, modified date.
If possible, you should sort the data directly from the API.
Otherwise, you can use the Array#sort function once you get the data
const arr = [{
_id: 10,
total: 990
}, {
_id: 11,
total: 20
}, {
_id: 6,
total: 448
}, {
_id: 9,
total: 700
}, {
_id: 8,
total: 100
}, {
_id: 7,
total: 900
}]
arr.sort((a, b) => a._id - b._id)
console.log(arr)
Related
I have an array of items, where i need to get a string of each product price.
const input = [{id: 1, amount: 20}, {id: 2, amount: 40}, {id: 3, amount: 90}]
const output = input?.reduce((acc, curr) => {
acc += `$${curr.amount}+`;
return acc;
}, '')
console.log(output)
Expected output is $20+$40+$90
But when i am trying this code i am getting the sum as $150 and i don't want to have + at the last if there are no more items.
Why Array.reduce()? This is a classic example for Array.map():
const input = [{id: 1, amount: 20}, {id: 2, amount: 40}, {id: 3, amount: 90}]
const expression = input.map(
({ amount }) => `$${amount}` // destructure the object, keep only .amount
).join('+');
console.log(expression);
Read about destructuring in the JavaScript documentation.
You can use map to extract the values followed by a join to create the string.
input.map(i => `$${i.amount}`).join('+')
Use split, and your code almost works
const input = [{id: 1, amount: 20}, {id: 2, amount: 40}, {id: 3, amount: 90}]
const output = input?.reduce((acc, curr) => {
acc.push(curr.amount + "$");
return acc;
}, []).join("+")
console.log(output)
const input = [{ id: 1, amount: 20 }, { id: 2, amount: 40 }, { id: 3, amount: 90 }]
const output = input?.slice(1).reduce((acc, curr) => {
acc += `+$${curr.amount}`;
return acc;
}, input.length ? `$${input[0].amount}` : '');
console.log(output)
with minimum manipulation
To add to the answers, we can use the currentIndex in the callback function in reduce as the third argument.
const input = [{id: 1, amount: 20}, {id: 2, amount: 40}, {id: 3, amount: 90}, {id: 4, amount: 55}]
const output = input?.reduce((acc, curr, index) => {
acc += `$${curr.amount}`;
if (index < input.length - 1) acc += '+'
return acc;
}, '')
console.log(output)
`
I have an array of records that contain objects, each with an id and an amount. Now, in this array, some elements have the same id. What I need to do is iterate over this array, and first, sum up the values for records that have the same id, and then return just one record when that is the case - so I end up with an array with only unique ids -- each one also containing an amount property.
In other words, I want to take this array:
const records = [
{id: 1, amount: 10},
{id: 1, amount: 20},
{id: 2, amount: 10},
{id: 3, amount: 10},
{id: 3, amount: -10}
];
... and produce this array:
const transformedRecords = [
{id: 1, amount: 30},
{id: 2, amount: 10},
{id: 3, amount: 0}
];
I've thought about using a for-of loop for this, but that might bet pretty verbose, and I'm guessing there's a more succinct way to accomplish this - perhaps with reduce()? What's an elegant way to approach this, preferably using es6+ syntax?
Use Array.reduce, for each iteration, check if you have an object with the current id in the accumulator, if you do, add the amounts, if not, push the current object to the accumulator :
const records = [
{id: 1, amount: 10},
{id: 1, amount: 20},
{id: 2, amount: 10},
{id: 3, amount: 10},
{id: 3, amount: -10},
{id: 4, amount: -10},
{id: 4, amount: -10}
];
const result = records.reduce((acc, curr) => {
const ndx = acc.findIndex(e => e.id === curr.id);
if(ndx > -1) {
acc[ndx].amount += curr.amount
}
else{
acc.push(curr)
}
return acc;
}, [])
console.log(result)
You can use reduce() to create an object and then use map() on its entries to create array of objects back
const records = [
{id: 1, amount: 10},
{id: 1, amount: 20},
{id: 2, amount: 10},
{id: 3, amount: 10},
{id: 3, amount: -10}
];
const res = Object.entries(records.reduce((ac, a) => {
ac[a.id] = (a[a.id] || 0) + a.amount;
return ac
}, {})).map(([id, amount]) => ({id, amount: amount < 0 ? 0 : amount}))
console.log(res)
Suppose we have and array of objects where each object represents a category or a subcategory, like this one:
var data = [
{type: "parent-category", order: 1, categoryId: 1},
{type: "parent-category", order: 2, categoryId: 2},
{type: "child-category", order: 1, categoryId: 3, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 4, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 5, parentCategoryId: 2},
{type: "child-category", order: 1, categoryId: 6, parentCategoryId: 2}
];
This array represent an example with two categories, where each one of they also have two subcategories.
Now, I'm facing with the problem of sort this array on some custom way. As you can note, the order attribute inside each object is related to the position (or ordering) of the object inside his level of hierarchy. And the relation between categories and subcategories is defined by the atributte parentCategoryId.
The expected sorting I need using as example the previous array of data should be this one:
var data = [
{type: "parent-category", order: 1, categoryId: 1},
{type: "child-category", order: 1, categoryId: 3, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 4, parentCategoryId: 1},
{type: "parent-category", order: 2, categoryId: 2},
{type: "child-category", order: 1, categoryId: 6, parentCategoryId: 2},
{type: "child-category", order: 2, categoryId: 5, parentCategoryId: 2}
];
My current approach to solve this problem is based on creating a map to numbers, analyzing the values of the attributes with the following conditions:
For an object that has the type parent-category we assign the value of categoryId * 1000.
For an object that has the class child-category we assign the value of (parentCategoryId * 1000) + order
This logic is shown on the next sort implementation:
let data = [
{type: "parent-category", order: 1, categoryId: 1},
{type: "parent-category", order: 2, categoryId: 2},
{type: "child-category", order: 1, categoryId: 3, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 4, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 5, parentCategoryId: 2},
{type: "child-category", order: 1, categoryId: 6, parentCategoryId: 2}
];
let orderedData = data.sort((a, b) =>
{
var aCat = (a.type == "parent-category") ? a.categoryId : a.parentCategoryId;
var aOrder = (a.type == "parent-category") ? 0 : a.order;
var bCat = (b.type == "parent-category") ? b.categoryId : b.parentCategoryId;
var bOrder = (b.type == "parent-category") ? 0 : b.order;
return (aCat * 1000 + aOrder) - (bCat * 1000 + bOrder);
});
console.log(orderedData);
However, and ignoring the fact that the previous implementation works, my question is if there exists a better approach or alternative to solve this problem. I don't like the idea of depend on the mapping to numbers, because, for example, the previous implementation introduces a limitation on the numbers of subcategories (999 in this case) I can sort correctly under each category. Thanks!
Simple performant solution using Array#filter Array#sort and Array#map
var data=[{type:"parent-category",order:1,categoryId:1},{type:"parent-category",order:2,categoryId:2},{type:"child-category",order:1,categoryId:3,parentCategoryId:1},{type:"child-category",order:2,categoryId:4,parentCategoryId:1},{type:"child-category",order:2,categoryId:5,parentCategoryId:2},{type:"child-category",order:1,categoryId:6,parentCategoryId:2}]
let res = data
.filter(({type}) => type === "parent-category")
.sort((a,b) => a.order - b.order)
.reduce((acc, curr) =>{
const children = data
.filter(({parentCategoryId}) => parentCategoryId === curr.categoryId)
.sort((a,b) => a.order - b.order);
acc.push(curr, ...children);
return acc;
}, []);
console.log(res);
If performance is not an issue, I'd prefer using a more step-by-step way:
/* ORIGINAL DATA */
const data = [
{type: "parent-category", order: 1, categoryId: 1},
{type: "parent-category", order: 2, categoryId: 2},
{type: "child-category", order: 1, categoryId: 3, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 4, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 5, parentCategoryId: 2},
{type: "child-category", order: 1, categoryId: 6, parentCategoryId: 2}
];
const sorted = [];
const byOrder = (a, b) => a.order - b.order;
// Get and sort parents first
const parents = data.filter(obj => obj.type === 'parent-category');
parents.sort(byOrder);
// Push to resulting array by parent order
parents.forEach(obj => {
sorted.push(obj);
// Get and sort corresponding children objs
const children = data.filter(o => o.parentCategoryId === obj.categoryId);
children.sort(byOrder);
// Push the objs into resulting array together
sorted.push.apply(sorted, children);
});
console.log(sorted);
This approach involves more steps, but is straightforward and can be easily understood compared to your complex sorting logic.
Using external libraries for only one function is overkill.
Based on #kemicofa answer. Just changed it to support more than two levels
function iterative(categories, parentCategory, level = 0) {
return categories
.filter((category) => parentCategory ? category.parent === parentCategory : !category.parent)
.map(category => {
category.level = level;
return category;
})
.sort((a,b) => a.name.localeCompare(b.name))
.reduce((acc, curr) =>{
const children = iterative(categories, curr.id, level+1)
acc.push(curr, ...children);
return acc;
}, [])
;
}
For easy way, use lodash#orderBy
If you don't prefer lodash, you can define your own specific function in sort
I have not fully understand your sorting criteria, but, for example,
function sort_categories(cat1,cat2) {
if (cat1["order"] < cat2["order"])
return -1;
if (cat1["order"] > cat2["order"])
return 1;
return 0;
}
data.sort(sort_categories);
will simply sort by orders.
For details about return values, take a look at here
If compareFunction is supplied, all non-undefined array elements are
sorted according to the return value of the compare function (all
undefined elements are sorted to the end of the array, with no call to
compareFunction). If a and b are two elements being compared, then:
If compareFunction(a, b) is less than 0, sort a to an index lower than b (i.e. a comes first).
If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other, but sorted with respect to all different
elements. Note: the ECMAscript standard does not guarantee this
behaviour, and thus not all browsers (e.g. Mozilla versions dating
back to at least 2003) respect this.
If compareFunction(a, b) is greater than 0, sort b to an index lower than a (i.e. b comes first).
compareFunction(a, b) must always return the same value when given a specific pair of elements a and b as its two arguments. If
inconsistent results are returned then the sort order is undefined.
This will work. You sort your categoryId first and then do your math by running the forEach function.
var data = [
{type: "parent-category", order: 1, categoryId: 1},
{type: "parent-category", order: 2, categoryId: 2},
{type: "child-category", order: 1, categoryId: 3, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 4, parentCategoryId: 1},
{type: "child-category", order: 2, categoryId: 5, parentCategoryId: 2},
{type: "child-category", order: 1, categoryId: 6, parentCategoryId: 2}
];
data.sort( function(a, b){
return a.categoryId - b.categoryId;
}).forEach( function(itm){
itm.categoryId = itm.categoryId * 1000;
if( itm.parentCategoryId){
itm.parentCategoryId = itm.parentCategoryId * 1000 + itm.order
}
});
console.log(data);
Or in ES6 fashion...
let orderedData = data.sort( (a, b) => {
return a.categoryId - b.categoryId;
}).forEach( itm => {
itm.categoryId = itm.categoryId * 1000;
if( itm.parentCategoryId){
itm.parentCategoryId = itm.parentCategoryId * 1000 + itm.order
}
});
Like this is my array of objects:
var x = [
{_id: 1, total: 25},
{_id: 1, total: 22},
{_id: 2, total: 4},
{_id: 2, total: 32},
{_id: 3, total: 56},
{_id: 4, total: 21},
{_id: 4, total: 58},
]
Now I want to achieve all total sum of similar object keys like this
[
{_id: 1, total: 47},
{_id: 2, total: 36},
{_id: 3, total: 25},
{_id: 4, total: 79},
]
Can anyone suggest how to do this on es6
Use reduce. reduce is an array method that can transform an array into something else, i.e. another array that can have different length. map will always return an array with the same number of elements. And filter can return an array that has less elements but the elements will be unchanged.
Reduce gives you the more flexible behavior. You can change the elements and you can store them in whatever way you like.
const result = x.reduce((acc, el) => {
const index = acc.findIndex(({_id}) => el._id === _id);
if (index > -1) {
acc[index].total += el.total;
} else {
acc.push({...el});
}
return acc;
}, [])
console.log(result);
In case if this code would run often and on large arrays, you can use more performant but a little more complex solution where we use a hash table to store the data:
var x = [
{_id: 1, total: 25},
{_id: 1, total: 22},
{_id: 2, total: 4},
{_id: 2, total: 32},
{_id: 3, total: 56},
{_id: 4, total: 21},
{_id: 4, total: 58},
]
const temp = {};
for (const el of x) {
temp[el._id] = (temp[el._id] || 0) + el.total;
}
const result = Object.entries(temp).map(([_id, total]) => ({_id, total}));
console.log(result);
But before starting optimizing you should always check if it's worth doing by running perf tools.
I am kinda new to javascript and today I encountered problem. Thing is, I have an array of amount of people visited every day by each hour (as you can see bellow). And I would like to find out most popular hour of day. My plan was to create a map where key is index of hour (0, 1, 2, 3, 4, 5...) and value is sum of all people who visited across all days on that hour. Problem is I'm not able to do that with my JS knowledge. Can someone give me a direction how to approach this problem? Thank you very much.
[
{date: "25.05.2018",
value: {
1: 209
2: 123
3: 890
.
.
24: 789
}
},
{date: "26.05.2018",
value: {
1: 280
2: 398
3: 450
.
.
24: 76
}
}
]
My JAVA like solution:
const { data: { data: [{ values }] } } = insightsData;
const timesMap = new Map();
values.forEach(item => {
Object.entries(item.value).forEach(([key, value]) => {
const timeValue = timesMap.get(key);
if (timeValue) {
timesMap.set(key, timeValue + value);
} else {
timesMap.set(key, value);
}
});
});
You could use Array#reduce with Array#map if you have arrays with the same length.
reduce takes an array as accumulator r and uses a logical OR || with a zero as value if an item does not exist in the accumulator.
var counts = [{ date: "26.05.2018", value: [125, 100, 200] }, { date: "27.05.2018", value: [5, 6, 7] }, { date: "28.05.2018", value: [3, 4, 5] }],
result = counts.reduce(
(r, { value }) => value.map((v, i) => (r[i] || 0) + v),
[]
);
console.log(result);
With objects as value properties.
var counts = [{ date: "26.05.2018", value: { 0: 125, 1: 100, 2: 200 } }, { date: "27.05.2018", value: { 0: 5, 1: 6, 2: 7 } }, { date: "28.05.2018", value: { 0: 3, 1: 4, 2: 5 } }],
result = counts.reduce(
(r, { value }) => Object
.entries(value)
.reduce((s, [k, v]) => {
s[k] = (s[k] || 0) + v;
return s;
}, r),
{}
);
console.log(result);
You can do iteratre over the values and add them to sum, like this
const data = {date: "26.05.2018",
value: [
125,
100,
200,
]
}
let sum = 0;
Object.values(data.value).forEach(elem => sum = sum + elem)
console.log(sum)
https://jsfiddle.net/5wwzn4yt/