Array of objects that I got
[
{
"id": 1,
"price": 100
},
{
"id": 1,
"price": 80
},
{
"id": 2,
"price": 8
},
{
"id": 1,
"price": 85
}
]
Array of objects that I am trying to do
[
{
"id": 1,
"price": 88.33 // AVERAGE VALUE BETWEEN DUPLICATED OBJECTS
},
{
"id": 2,
"price": 8
}
]
I am merging and getting the average price for duplicated objects.
What I have done:
I have tried to use filter() function but I removed the duplicated without merging the prices.
If you want to avoid extra loops and extra properties is not a problem, you can use a getter for each object as follow:
You can use the function Array.prototype.reduce for grouping objects by id and the function Object.values for extracting the grouped values.
The getter price calculates the average when this property is accessed.
Extra properties:
{
count: Integer // count of repeated ids.
sum: Double // total sum of prices
}
const arr = [ { "id": 1, "price": 100 }, { "id": 1, "price": 80 }, { "id": 2, "price": 8 }, { "id": 1, "price": 85 } ],
result = Object.values(arr.reduce((r, {id, price}) => {
let current = (r[id] || (r[id] = {id, sum: 0, count: 0, get price() {
return this.sum / this.count;
}}));
current.sum += price;
current.count++;
return r;
}, {}));
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use .reduce() with an ES6 Map. By using reduce() you can accumulate all objects into a Map, where the key is the id from the object and the value is an accumulated array of price values for the given id. You can then convert the Map back into an array using Array.from(), where you can provide a mapping function to convert the [key, value] pairs from the map into an object. The object's price key will be the sum of all numbers in the value array (arr) divided by the length of the array, which will give you the average.
See example below:
const arr = [ { "id": 1, "price": 100 }, { "id": 1, "price": 80 }, { "id": 2, "price": 8 }, { "id": 1, "price": 85 } ];
const res = Array.from(arr.reduce((m, {id, price}) => {
return m.set(id, [...(m.get(id) || []), price]);
}, new Map), ([id, arr]) => ({id, price: arr.reduce((t, n) => t+n, 0) / arr.length}));
console.log(res);
Use forEach loop and build an object with keys as id and aggregate price.
Use Object.values of above object and calculate the averages.
const data = [
{
id: 1,
price: 100,
},
{
id: 1,
price: 80,
},
{
id: 2,
price: 8,
},
{
id: 1,
price: 85,
},
];
const process = (arr) => {
const res = {};
arr.forEach(({ id, price }) => {
res[id] ??= { id, sum: 0, count: 0 };
res[id].sum += price;
res[id].count += 1;
});
return Object.values(res).map(({ id, sum, count }) => ({
id,
price: sum / count,
}));
};
console.log(process(data));
Related
Products Array has an array property called subArr and my goal is to return an array with the length of subArr which will include biggest numbers.
Array
const products = [
{
name: "car",
subArr: ["4", "200", "599.4", "4444"]
},
{
name: "tv",
subArr: ["44477", "50", "579.2", "3232"]
},
{
name: "glass",
subArr: ["2121.1", "6347", "8867", "90.01"]
}
];
My desired array is [44477, 4444, 8867, 6347]
I tried to map through the main array and the loop through the second one but can't figure out how to get an array with the length of subArr
const products = [
{
name: "car",
numArr: ["4", "200", "599.4", "4444"]
},
{
name: "tv",
numArr: ["44477", "50", "579.2", "3232"]
},
{
name: "glass",
numArr: ["2121.1", "6343", "8867", "90.01"]
}
];
function getMaxFromArr(products) {
if (!products.length) {
return [];
}
return products[0].numArr.map((val, index) => {
return products.map((prod) => parse(prod.numArr[index]));
});
}
const parse = value => parseFloat(value);
const result = getMaxFromArr(products);
console.log("result", result);
Any help will be appreciated
Approach:
First merge the all subArrs by reduce()
Convert them to numbers using .map(Number)
Sort the newArray and finally slice() them.
const products = [ { name: "car", subArr: ["4", "200", "599.4", "4444"] }, { name: "tv", subArr: ["44477", "50", "579.2", "3232"] }, { name: "glass", subArr: ["2121.1", "6347", "8867", "90.01"] } ];
const sortNum = (a, b) => b - a; //descending order
const findMaxArr = (arr, sArrSize) => products.reduce((a, {subArr}) => [...a, ...subArr.map(Number)],[]).sort(sortNum).slice(0, sArrSize);
console.log(findMaxArr(products, products[0].subArr.length));
Judging by your current code you're trying to "zip" the arrays within your products, but for each "column"/index that you zip, you want to grab the max value. That could be achieved by taking the max of your inner array with Math.max() and spreading (...) the mapped numbers into that. You can remove the parse() method as Math.max() will parse the strings to numbers internally.
See your modified code below (I've also modified it to use optional chaining (?.) and the nullish coalescing (??), but you can keep it to use the same if-statement you had if you wish):
const products = [ { name: "car", subArr: ["4", "200", "599.4", "4444"] }, { name: "tv", subArr: ["44477", "50", "579.2", "3232"] }, { name: "glass", subArr: ["2121.1", "6347", "8867", "90.01"] } ];
function getMaxFromArr(products) {
return products[0]?.subArr.map((val, index) =>
Math.max(...products.map((prod) => prod.subArr[index]))
) ?? [];
}
const result = getMaxFromArr(products);
console.log("result", result);
Get all the numbers to a single array using flatMap and convert them to numbers using Number
sort the array of numbers in descending order
take the top n items using slice
const products = [{name:"car",numArr:["4","200","599.4","4444"]},{name:"tv",numArr:["44477","50","579.2","3232"]},{name:"glass",numArr:["2121.1","6343","8867","90.01"]}],
topN = products
.flatMap(p => p.numArr.map(Number))
.sort((a, b) => b - a)
.slice(0, products[0].numArr.length)
console.log(topN)
const products = [{
name: "car",
numArr: ["4", "200", "599.4", "4444"]
},
{
name: "tv",
numArr: ["44477", "50", "579.2", "3232"]
},
{
name: "glass",
numArr: ["2121.1", "6343", "8867", "90.01"]
}
];
function getMaxFromArr(products) {
var BiggestNum = 0;
var BiggestNumArray = [];
if (!products.length) {
return [];
}
products.map((value, index) => {
var Array = value.numArr;
for (let i = 0; i < Array.length; i++) {
if (BiggestNum < Number(Array[i])) {
BiggestNum = Number(Array[i])
BiggestNumArray = Array
}
}
})
return BiggestNumArray
}
const parse = value => parseFloat(value);
const result = getMaxFromArr(products);
console.log("result", result);
I tried not to modify to much your code. You can read the logic in the comments:
Get the length of first numArr
Convert all strings in all numArr props to numbers and save them in a new array
Sort and slice
const products = [
{
name: "car",
numArr: ["4", "200", "599.4", "4444"]
},
{
name: "tv",
numArr: ["44477", "50", "579.2", "3232"]
},
{
name: "glass",
numArr: ["2121.1", "6343", "8867", "90.01"]
}
];
function getMaxFromArr(products) {
let allNumbers = []
if (!products.length) {
return []
}
else{
// get the length of first numArr
let subArrLength = products[0].numArr.length
// convert all strings in all numArr props to numbers and save them in a new array
products.forEach((prod) => {
prod.numArr.forEach((n) => allNumbers.push(Number(n)))
})
// sort and slice
console.log(allNumbers.sort((a,b) => a - b).slice(allNumbers.length - subArrLength))
}
}
getMaxFromArr(products)
I am trying to categorise the objects by comparing two objects say data and categories
const data = {
"1a": {
"name": "1a",
"count": 154
},
"1b": {
"name": "1b",
"count": 765
},
"1c": {
"name": "1c",
"count": 7877
},
"777": {
"name": "777",
"count": 456
}
};
const categories = {
"A_category":["A","1a", "2a"],
"B_category":["1b", "2b"],
"C_category":["1c", "2c"],
"D_category":["1d", "2d"]
};
I want to group the data based on the category object, when there is no match the group should be others and the resultant data should be like
const resultData = [
{ group: 'Others', name: '777', count: 456 },
{ group: 'A_category', name: '1a', count: 154 },
{ group: 'B_category', name: '1b', count: 765 },
{ group: 'C_category', name: '1c', count: 7877 }
]
I used the function but not able to achieve the result
const resultData = [];
function restructure(data, categories) {
Object.keys(data).map(
dataKey => {
for (let [key, value] of Object.entries(categories)) {
value.includes(dataKey) ? resultData.push({"group": key,...data[dataKey]}) : resultData.push({"group": "Others",...data[dataKey]}) ;
break;
}
}
)
}
restructure(data,categories);
You can try this as well. Iterate over your data entries and find whether the key exists in any of the categories object data and push it into the array with found category as group or push it with Others as group as shown in the below code
const data = {
"1a": {
"name": "1a",
"count": 154
},
"1b": {
"name": "1b",
"count": 765
},
"1c": {
"name": "1c",
"count": 7877
},
"777": {
"name": "777",
"count": 456
}
};
const categories = {
"A_category": ["A", "1a", "2a"],
"B_category": ["1b", "2b"],
"C_category": ["1c", "2c"],
"D_category": ["1d", "2d"]
};
const resultData = [];
Object.entries(data).map(([key, val])=>{
let group = Object.keys(categories).find(category=>categories[category].includes(key)) || 'Others'
resultData.push({
group,
...val
})
})
console.log(resultData)
Instead of for loop you need to use filter as let category = Object.entries(categories).filter(([key, value]) => value.includes(dataKey));.
If category.length > 0 then category is available else use Others.
Try it below.
const data = {
"1a": {
"name": "1a",
"count": 154
},
"1b": {
"name": "1b",
"count": 765
},
"1c": {
"name": "1c",
"count": 7877
},
"777": {
"name": "777",
"count": 456
}
};
const categories = {
"A_category": ["A", "1a", "2a"],
"B_category": ["1b", "2b"],
"C_category": ["1c", "2c"],
"D_category": ["1d", "2d"]
};
const resultData = [];
function restructure(data, categories) {
Object.keys(data).map(
dataKey => {
let category = Object.entries(categories)
.filter(([key, value]) => value.includes(dataKey));
resultData.push({
"group": category.length > 0 ? category[0][0] : "Others",
...data[dataKey]
});
})
}
restructure(data, categories);
console.log(resultData);
That's because you're breaking out of the loop regardless of whether you found the category or not. Your for loop will only execute once then breaks immediately. If the first category object matches, it is used, if not "Others" is assigned and the loop exits without checking the rest of the categories. Only break out of the loop if the lookup is successful:
for (let [key, value] of Object.entries(categories)) {
if(value.includes(dataKey)) { // if this is the category
resultData.push({ "group": key, ...data[dataKey] }); // use it ...
return; // ... and break the loop and the current iteration of forEach. The current object is handled
}
}
resultData.push({ "group": "Others", ...data[dataKey] }); // if the return statement above is never reached, that means the category was not found, assign "Others"
BTW, you can use other array methods to shorten things out like so:
function restructure(data, categories) {
return Object.keys(data).map(key => ({
"group": Object.keys(categories).find(cat => categories[cat].includes(key)) || "Others",
...data[key]
}));
}
Then use like so:
const resultData = restructure(data, categories);
My method uses find to try to find a category key that contains the name of the object, if find fails, it returns null at which point, the || "Others" part is evaluated and "Others" will be used as the group name (Does JavaScript have "Short-circuit" evaluation?).
Demo:
const data = {"777":{"name":"777","count":456},"1a":{"name":"1a","count":154},"1b":{"name":"1b","count":765},"1c":{"name":"1c","count":7877}};
const categories = {"A_category":["A","1a","2a"],"B_category":["1b","2b"],"C_category":["1c","2c"],"D_category":["1d","2d"]};
function restructure(data, categories) {
return Object.keys(data).map(key => ({
"group": Object.keys(categories).find(cat => categories[cat].includes(key)) || "Others",
...data[key]
}));
}
const resultData = restructure(data, categories);
console.log(resultData);
I need to make a function to iterate an array of x objects then compare the date inside the objects and separate in different arrays so I can show separately in the HTML, this is my object:
[{"id":1,"date":"2020-02-06","value":131},{"id":2,"date":"2020-02-06","value":135},{"id":3,"date":"2020-02-06","value":141},{"id":4,"date":"2020-02-05","value":151},{"id":6,"date":"2020-02-05","value":155}]
I want something like this:
obj1 = [{"id":1,"date":"2020-02-06","value":131},{"id":2,"date":"2020-02-06","value":135},{"id":3,"date":"2020-02-06","value":141}]
obj2 = [{"id":4,"date":"2020-02-05","value":151},{"id":6,"date":"2020-02-05","value":155}]
my code:
// global variables
json = [{
"id": 1,
"date": "2020-02-06",
"value": 131
}, {
"id": 2,
"date": "2020-02-06",
"value": 135
}, {
"id": 3,
"date": "2020-02-06",
"value": 141
}, {
"id": 4,
"date": "2020-02-05",
"value": 151
}, {
"id": 6,
"date": "2020-02-05",
"value": 155
}];
obj1 = [];
obj2 = [];
for (const x of json) {
if (x.date != x.date) {
obj1.push(x)
} else {
obj2.push(x)
}
}
console.log(obj1);
console.log(obj2);
in result always the items push into the obj1..
any help is welcome.
The typical solution for this is to group them by the key and push them to an array. Below is an example using Array reduce and Object.values to get it down to the two arrays.
var items = [
{"id":1,"date":"2020-02-06","value":131},
{"id":2,"date":"2020-02-06","value":135},
{"id":3,"date":"2020-02-06","value":141},
{"id":4,"date":"2020-02-05","value":151},
{"id":6,"date":"2020-02-05","value":155}
]
var dateGroups = items.reduce( function (dates, item) {
dates[item.date] = dates[item.date] || []
dates[item.date].push(item)
return dates
}, {})
var results = Object.values(dateGroups)
console.log(results)
You could goup by date and get an array of arrays.
var data = [{ id: 1, date: "2020-02-06", value: 131 }, { id: 2, date: "2020-02-06", value: 135 }, { id: 3, date: "2020-02-06", value: 141 }, { id: 4, date: "2020-02-05", value: 151 }, { id: 6, date: "2020-02-05", value: 155 }],
grouped = data.reduce((r, o) => {
var group = r.find(([{ date }]) => date === o.date);
if (!group) r.push(group = []);
group.push(o);
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use reduce to build an object that maps each unique date to its respective data items, having, basically, a group by date:
const data = [{"id":1,"date":"2020-02-06","value":131},{"id":2,"date":"2020-02-06","value":135},{"id":3,"date":"2020-02-06","value":141},{"id":4,"date":"2020-02-05","value":151},{"id":6,"date":"2020-02-05","value":155}];
const groupedData = data.reduce((acc, curr) => ({
...acc,
[curr.date]: [...(acc[curr.date] || []), curr]
}), {});
console.log(groupedData);
We also use above the spread syntax and computed property names to make the code shorter.
This is a follow up to again Push same multiple objects into multiple arrays.
AFter I create my objects:
let objName = ["object1", "object2", "object3"];
let xyzArr = ["xyz1", "xyz2", "xyz3"];
let theArr = [[], [], []];
let objects = [];
objName.forEach((name, index) => {
objects.push({
xyz: xyzArr[index],
arr: theArr[index]
});
});
And push values using #NickParsons solution:
$.getJSON(json, result => {
result.forEach(elem => {
objects.forEach(obj => {
obj.arr.push({
x: elem.date,
y: elem.val2
});
});
});
});
Here I am adding my objects, i.e. x and y based on no condition. But I want to add it based on if indexOf(obj.xyz) = elem.val1.
THis is my JSON:
[
{
"date": "2019-07-21",
"val1": "xyz1_hello",
"val2": 803310
},
{
"date": "2019-07-22",
"val1": "xyz2_yellow",
"val2": 23418
},
{
"date": "2019-07-22",
"val1": "xyz1_hello",
"val2": 6630
},
{
"date": "2019-07-24",
"val1": "xyz2_yellow",
"val2": 4
},
{
"date": "2019-07-21",
"val1": "xyz3_yo",
"val2": 60984
}
]
Is there a way for me to push values to x and y if obj.xyz is LIKE (indexOF) elem.val1 For example, if indexOf(obj.xyz) = elem.val1, then push their corresponding elem.date and elem.val2 data to obj.arr.
Assuming you have some boolean like(a,b) function that decides if two values are similar or not, and your elements are in elems:
objects.forEach(o =>
elems.forEach(e => {
if(like(o.xyz, e.val1)){
o.arr.push({
x: e.date,
y: e.val2
});
}
}));
Any suggestion on how to do grupby + sortby together using UnderscoreJS?
Having following collection, I would like to find smallest price per season:
var d = [{
"price": 27.25,
"season": 2
},
{
"price": 10,
"season": 3
},
{
"price": 21,
"season": 2
}];
I was able to group using the below:
_.chain(d).groupBy('season').map(function(value, key) {
return {
season: key,
obj: value
}
}).value();
which gets me:
[
{"season":"2","obj":[{"price":27.25,"season":2},{"price":21,"season":2}]},
{"season":"3","obj":[{"price":10,"season":3}]}
]
Try this:
var output = _.chain(d).groupBy("season").map(function (group) {
return _.chain(group).sortBy("price").first().value();
}).value();
See the demo: http://jsfiddle.net/Ns2MJ/
Get the minimum price of each season in your map function:
_.chain(d).groupBy('season').map(function(value, key) {
return {
season: key,
price: _.chain(value).pluck('price').min().value()
}
}).sortBy('price').value();
You can use _min combined with _.property to extract the smallest price in a group:
var output = _.chain(d).groupBy('season').map(function (group) {
return _.min(group, _.property('price'));
}).value();
And a demo http://jsfiddle.net/nikoshr/Edf5g/