Sort array with Objects inside of Objects - javascript

I have this array:
[
["name1", { count: 20 }],
["name2", { count: 10 }]
]
How would I go about sorting this array by the value of count?
I have tried using the sort function,
const sort = Array.sort((a, b) => b.count - a.count);
But this didn't change anything.

You need to access the second entry in the arrays inside the outer array. Your code is using count on the array entries, but they don't have a count property:
theArray.sort((a, b) => b[1].count - a[1].count);
Note also that you call sort on the actual array, not the Array constructor. It also sorts the array in-place, rather than returning a sorted array (it also returns the array you call it on, though).
Live Example:
const theArray = [
["name1", { count: 20 }],
["name2", { count: 10 }],
["name3", { count: 15 }]
];
console.log("before:", theArray);
theArray.sort((a, b) => b[1].count - a[1].count);
console.log("after:", theArray);
.as-console-wrapper {
max-height: 100% !important;
}

Related

Is there a better way to sum up values of a nested array?

I currently have the following items and implementation below. I am not sure if using reduce within another reduce is performant. Is there a better way to sum up nested arrays?
const items = [
{
id: 111,
fruits: [
{
name: "apple",
total: 5
},
{
name: "pineapple",
total: 1
}
]
},
{
id: 222,
fruits: [
{
name: "kiwi",
total: 2
}
]
}
];
// my implementation using reduce within another to get the sum of totals.
const sumOfFruits = items
.reduce((sum, curr) => sum + curr.fruits
.reduce((fruitSum, fruitCurr) => fruitSum + fruitCurr.total));
console.log(sumOfFruits);
// returns 8
A cleaner (not necessarily faster) way to do this would be:
collect all "fruits" (flatMap)
pick "totals" (map)
sum the result
const items = [
{
id: 111,
fruits: [
{
name: "apple",
total: 5
},
{
name: "pineapple",
total: 1
}
]
},
{
id: 222,
fruits: [
{
name: "kiwi",
total: 2
}
]
}
];
//
res = items
.flatMap(x => x.fruits)
.map(x => x.total)
.reduce((s, n) => s + n, 0)
console.log(res)
Regarding performance, the thing about javascript is that it's performant enough unless you have millions of objects. And if you do have millions of objects, you shouldn't be using javascript in the first place.
Your code does not produce the desired output: it coerces an object to string and performs string concatenation.
This is because reduce is called without second argument, and thus the accumulator gets the first object as value, while you want the accumulated value to be a number.
So you need to add 0 as second argument for the outer reduce call. For the inner reduce call you can improve a little bit and provide sum as initial value. That way you don't have to do sum + anymore.
You can also make use of destructuring in the callback parameters:
This leads to the following code:
const items = [{id: 111,fruits: [{name: "apple",total: 5},{name: "pineapple",total: 1}]},{id: 222,fruits: [{name: "kiwi",total: 2}]}];
const sumOfFruits = items.reduce(
(sum, {fruits}) => fruits.reduce(
(fruitSum, {total}) => fruitSum + total,
sum // Continue with the sum we already have
), 0 // Start with 0 for the accumulator
);
console.log(sumOfFruits); // 8
Many would agree that this is how it should be done. If performance really is an issue, then you can get a slight improvement by using plain old for loops. These do not use callbacks, and so can be expected to do the job a little bit faster, but with less elegant code. In my experience they also perform a tiny bit faster than for..of loops:
var items = [{id: 111,fruits: [{name: "apple",total: 5},{name: "pineapple",total: 1}]},{id: 222,fruits: [{name: "kiwi",total: 2}]}];
var sumOfFruits = 0;
for (var i = 0; i < items.length; i++) {
var fruits = items[i].fruits;
for (var j = 0; j < fruits.length; j++) {
sumOfFruits += fruits[j].total;
}
}
console.log(sumOfFruits); // 8
It probably is not worth the millisecond you would gain from this with normal sized input.

How to merge an array of objects into one array and then filter out the duplicates

Firstly, I am trying to merge an array of many objects into a single array with every key in each object.
Lastly, any duplicate items in the array should be removed as well as any elements named "name".
Input:
const data = [
{
name: '10/20',
Tyler: 1,
Sonia: 0,
Pedro: 0,
},
{
name: '10/23',
Tyler: 0.5,
Sonia: 0.25,
Pedro: 0.75,
George: 0.5,
},
];
Output:
["Tyler", "Sonia", "Pedro", "George"]
This is what I've tried so far:
const mergedData = data.reduce((prev, cur) => {
const obj = cur[0];
const keys = Object.keys(obj);
const names = keys.splice(1);
return { names };
}, []);
I am trying to capture any key name other than "name" and add it to the final array. However, this is where I get stuck because I get this error, TypeError: Cannot convert undefined or null to object
Note: Objects may be different lengths, contain a mix of names, but never any duplicates.
An option is to find all keys put in a set and remove the name key
const data = [
{
name: '10/20',
Tyler: 1,
Sonia: 0,
Pedro: 0,
},
{
name: '10/23',
Tyler: 0.5,
Sonia: 0.25,
Pedro: 0.75,
George: 0.5,
},
];
const set = new Set(data.reduce((acc, i) => [...acc, ...Object.keys(i)], []));
set.delete('name');
const result = [...set];
console.log(result);
If you have access to ES6 methods, you can do this using a Set (unique values are ensured at creation) and converting it back into an array if you want through Destructuring.
data = [{name: '0', Tyler: '1', Dan: '2', Carl: '3'}, {name: '0', Tyler: '1', Dan: '2', Eric: '3', Danny: '4'}];
const output = (data) => {
let output = [];
// This makes sure you get each array and then strips just the keys as desired
data.forEach(item => {
output = output.
concat(Object.keys(item))
});
// This creates the set, strips our dups, and then spreads itself into an array
return [...new Set(output)]
// Strip out the 'name' key as needed
// NOTE: This should be a param instead of hard-coded, but this is easier to show
.filter(res => res != 'name');
}
console.log(output(data));
This should be fairly performant considering it only navigates the full array one time and each object itself shouldn't have millions of properties to cause .keys() any issues.

How to map through an array of objects and add value to each other in React?

I would like to sum one specific value from my array of objects.
my array:
const myArray = [
{package_id: 1, width: "30"},
{package_id: 2, width: "20"},
{package_id: 3, width: "50"}
]
The value I would like to achive is the width. So the result would be 100
Is there any way to do this?
You can do it easily with a reduce function.
yourArray.reduce((acc, curr) => {
return acc + parseInt(curr.width, 10);
}, 0);

Convert multidimensional array to objects using dynamically generated and predefined array of items I want to set as keys

Are the steps I'm taking to solve this problem correct?
I'm working on turning a data structure of an array of arrays such as
this.arrayofAnimalsAndValues = [
[ "Ant", 1287, 12956],
[ "Lion", 2574, 25826],
[ "Bear", 3861, 38696],
.....
]
into this
this.jsonOfAnimalsAndValues = [
{category: "Ant", value_1: 1287, value_2:12956},
{category: "Lion", value_1: 2574, value_2:25826},
{category: "Bear", value_1: 3861, value_2:38696},
.....
]
where the first item in the array is always assigned to 'category' in the array object and the following items assigned to value_# depending on their order in the array. So the 2nd array item would have key value_1 and so on. For example, for 1 nested array:
[[ "Ant", 5148, 51566]] to =>
[{category: "Ant", value_1: "5148", value_2: 51566}]
I've created a hardcoded way to achieve this however I'm trying to make it dynamic:
'hardcoded' way:
this.variableOfKeys = ["value_1", "value_2", "value_3", ......]
this.jsonOfAnimalsAndValues = this.arrayofAnimalsAndValues(function(x) {
return {
category: x[0],
no1: x[1],
no2: x[2],
.....
};
});
where I just hardcode the keys and their values (values using their index).
My attempt to make it dynamic:
this.variableOfKeys.forEach(element => {
this.jsonOfAnimalsAndValues = this.arrayofAnimalsAndValues.map(function(x) {
for (var i = 0; i<=this.arrayOfValuesToUseAsKeys.length; ++i) {
return {
category: x[0],
element: x[i+1],
};
}
});
});
where my logic is that for each item in
this.variableOfKeys = ["value_1", "value_2", "value_3", ......],
I created this.jsonOfAnimalsAndValues such that the first item (item with the 0th index) in the array this.arrayofAnimalsAndValues is assigned to the key category and the following items (depending on their index) are assigned to the values in this.variableOfKeys in order starting from no1, then no2 etc.
However, I don't think this is written correctly and I keep getting this error:
"TypeError: Cannot read property 'variableOfKeys' of undefined"
Can I ask how it might be written incorrectly and so how I might be able to create this.jsonOfAnimalsAndValues from this.arrayofAnimalsAndValues?
You can map each subarray to an array of entries, then turn it into an object to return with Object.fromEntries:
const arrayofAnimalsAndValues = [
[ "Ant", 1287, 12956],
[ "Lion", 2574, 25826],
[ "Bear", 3861, 38696],
];
const output = arrayofAnimalsAndValues.map(
([category, ...rest]) => Object.fromEntries([
['category', category],
...rest.map((value, i) => ['value_' + (i + 1), value])
])
);
console.log(output);
Given an array of the keys you can map() the main array and use reduce() on each subarray to generate each object
const arr=[["Ant",1287,12956],["Lion",2574,25826],["Bear",3861,38696]],
keys = ['category','value_1','value_2'];
const res = arr.map(e => e.reduce((a,c,i) => (a[keys[i]] = c, a),{}))
console.log(res)
.as-console-wrapper { max-height: 100%!important;top:0}

Returning data after sorting through multiple arrays

I have nested arrays within an object, and I need to extract these values, and sort them into alphabetical order which will then be displayed in a table.
I am using the localeCompare method but it is returning:
Cannot read property 'localeCompare' of null
To be honest, I am not sure I am approaching this correctly. Using sort you can compare a to b to sort them into alphabetical order. I am getting confused how to compare the values from the arrays within a and b. I am using the first sort on tableData to access the array, and the using a second sort to compare the values that I pushed to array clientRefArr
if(params.sorting().clientRef) {
var clientRefArr = [];
tableData.sort(function(a, b){
a.renewalUIs.forEach(function(data, i){
clientRefArr.push(data.patentUI.clientRef);
})
return clientRefArr.sort(function(a, b){
console.log(a.localeCompare(b))
// return a.localeCompare(b)
})
})
orderedData = tableData;
}
return orderedData;
Question
Why is the error Cannot read property 'localeCompare' of null being returned?Am I approaching this issue completely wrong?
//JSON
[
0: {
transRef: "IX1000013"
renewalProgress: 30
renewalUIs: [
0: {
patentUI: {
business: null
clientRef: P0101011 // this is the required value
}
renewalAttemptsMade: 1
renewalDueDate: 1514764740000
}
]
},
1: {
transRef: "IX100333"
renewalProgress: 55
renewalUIs: [
0: {
patentUI: {
business: null
clientRef: P0101011 // this is the required value
}
renewalAttemptsMade: 1
renewalDueDate: 1514764740000
},
1: {
patentUI: {
business: null
clientRef: P5551011 // this is the required value
}
renewalAttemptsMade: 3
renewalDueDate: 174834740000
}
]
}
]
You could take a default value for both parts
(a || '').localeCompare(b || '')
for sorting the null values or even all falsy values to top.
An attempt with the given data (I changed some value, to get some sorting).
It now sorts the inner arrays renewalUIs first and then it takes the first element for sorting the outer array.
var array = [{ transRef: "IX1000013", renewalProgress: 30, renewalUIs: [{ patentUI: { business: null, clientRef: "P9101011" }, renewalAttemptsMade: 1, renewalDueDate: 1514764740000 }] }, { transRef: "IX100333", renewalProgress: 55, renewalUIs: [{ patentUI: { business: null, clientRef: "P7101011" }, renewalAttemptsMade: 1, renewalDueDate: 1514764740000 }, { patentUI: { business: null, clientRef: "P5551011" }, renewalAttemptsMade: 3, renewalDueDate: 174834740000 }] }];
array.forEach(function (o) {
o.renewalUIs.sort(function (a, b) {
return a.patentUI.clientRef.localeCompare(b.patentUI.clientRef);
});
});
array.sort(function (a, b) {
return a.renewalUIs[0].patentUI.clientRef.localeCompare(b.renewalUIs[0].patentUI.clientRef);
});
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
IF my interpretation is correct, it'd be way easier to first put everything in an single array and then sort it as such just use loops for the first task and a normal sort for the second, nested sorts are really just a bad idea.
// let's assume your data is in unorderedData
var clientUIs = [];
for (var i = 0; i < unorderedData.length; i++){
for (var j = 0 ; j < unorderedData[i]["renewalUIs"].length; j++){
// add each UI to your list individually.
clientUIs.push(unorderedData[i]["renewalUIs"][j]["patentUI"]);
}
}
clientUIs.sort() //just sort everything
tableData = clientUIs;
If "renewalUIs" or "patentUIs" arent constant you can iterate over the keys of the dictionary.

Categories