ES6: Find an object in an array by one of its properties - javascript

I'm trying to figure out how to do this in ES6...
I have this array of objects..
const originalData=[
{"investor": "Sue", "value": 5, "investment": "stocks"},
{"investor": "Rob", "value": 15, "investment": "options"},
{"investor": "Sue", "value": 25, "investment": "savings"},
{"investor": "Rob", "value": 15, "investment": "savings"},
{"investor": "Sue", "value": 2, "investment": "stocks"},
{"investor": "Liz", "value": 85, "investment": "options"},
{"investor": "Liz", "value": 16, "investment": "options"}
];
..and this new array of objects where I want to add each person's total value of their investment types (stocks, options, savings)..
const newData = [
{"investor":"Sue", "stocks": 0, "options": 0, "savings": 0},
{"investor":"Rob", "stocks": 0, "options": 0, "savings": 0},
{"investor":"Liz", "stocks": 0, "options": 0, "savings": 0}
];
I loop through originalData and save each property of the "current object" in a let..
for (let obj of originalData) {
let currinvestor = obj.investor;
let currinvestment = obj.investment;
let currvalue = obj.value;
..but here I want to find the obect in newData that has the property = currinvestor (for the "investor" key)
...then add that investment type's (currinvestment) value (currvalue)
}

newData.find(x => x.investor === investor)
And the whole code:
const originalData = [
{ "investor": "Sue", "value": 5, "investment": "stocks" },
{ "investor": "Rob", "value": 15, "investment": "options" },
{ "investor": "Sue", "value": 25, "investment": "savings" },
{ "investor": "Rob", "value": 15, "investment": "savings" },
{ "investor": "Sue", "value": 2, "investment": "stocks" },
{ "investor": "Liz", "value": 85, "investment": "options" },
{ "investor": "Liz", "value": 16, "investment": "options" },
];
const newData = [
{ "investor": "Sue", "stocks": 0, "options": 0, "savings": 0 },
{ "investor": "Rob", "stocks": 0, "options": 0, "savings": 0 },
{ "investor": "Liz", "stocks": 0, "options": 0, "savings": 0 },
];
for (let {investor, value, investment} of originalData) {
newData.find(x => x.investor === investor)[investment] += value;
}
console.log(newData);
.as-console-wrapper.as-console-wrapper { max-height: 100vh }

I would use some derivative of this:
var arrayFindObjectByProp = (arr, prop, val) => {
return arr.find( obj => obj[prop] == val );
};

Related

setting object key dynamically in javascript

I have an object like this:
let inputData = {
"dimensions": [
{
"id": "dimension_re",
"label": "Region",
"values": ["East", "East", "West", "SouthWest", "South","NorthEast"]
},
{
"id": "dimension_cnt",
"label": "County",
"values": ["London", "Italy", "Germany", "US", "Russia","India"]
},
{
"id": "measure_sales",
"label": "Sales",
"values": [100, 156, 432, 462, 25,100]
},
{
"id": "measure_qty",
"label": "Quantity",
"values": [85, 34, 153, 434, 52, 43]
},
{
"id": "measure_profit",
"label": "Profit",
"values": [123, 45, 421, 465, 451, 56]
}
]
}
My expected output:
let expectdData = [
{
"Region": "East",
"County": "London",
"Sales": 100,
"Quantity": 85,
"Profit": 123
},
{
"Region": "East",
"County": "Italy",
"Sales": 156,
"Quantity": 34,
"Profit": 45
},
{
"Region": "West",
"County": "Germany",
"Sales": 432,
"Quantity": 153,
"Profit": 421
},
{
"Region": "SouthWest",
"County": "US",
"Sales": 462,
"Quantity": 434,
"Profit": 465
},
{
"Region": "South",
"County": "Russia",
"Sales": 25,
"Quantity": 52,
"Profit": 451
},
{
"Region": "NorthEast",
"County": "India",
"Sales": 100,
"Quantity": 43,
"Profit": 56
}
]
Here is my program to get this expected data:
let actualData = [];
inputData.dimensions.forEach((e,i) => {
let tempVal = e.label;
e.values.forEach((elem,index) => {
actualData[index] = new Object({
[tempVal] : elem
});
})
});
console.log(actualData);
But unfortunately, I only get the last item for every object. In my console it looks like this:
[
{ Profit: 123 },
{ Profit: 45 },
{ Profit: 421 },
{ Profit: 465 },
{ Profit: 451 },
{ Profit: 56 }
]
I think, in every iteration, it just overrides the "tempVal" variable. How to prevent this & how can I achieve the expected array of objects?
You are replacing the whole object on each iteration, you just need to create it if it does not exists, otherwise you can replace the key.
let actualData = [];
inputData.dimensions.forEach((e,i)=>{
let tempVal = e.label;
e.values.forEach((elem,index) => {
if (!actualData[index]) {
actualData[index] = {}
}
actualData[index][tempVal] = elem
})
});
console.log(actualData);
Using Array#reduce, iterate over dimension while updating a list of the resulting objects
In each iteration, use Array#forEach to iterate over the current values list and update the object at each index with label as key and the current value as value
const inputData = {
"dimensions": [
{
"id": "dimension_re",
"label": "Region",
"values": ["East", "East", "West", "SouthWest", "South","NorthEast"]
},
{
"id": "dimension_cnt",
"label": "County",
"values": ["London", "Italy", "Germany", "US", "Russia","India"]
},
{
"id": "measure_sales",
"label": "Sales",
"values": [100, 156, 432, 462, 25,100]
},
{
"id": "measure_qty",
"label": "Quantity",
"values": [85, 34, 153, 434, 52, 43]
},
{
"id": "measure_profit",
"label": "Profit",
"values": [123, 45, 421, 465, 451, 56]
}
]
};
const res = inputData.dimensions.reduce((acc, { label, values = [] }) => {
values.forEach((value, index) => {
acc[index] = { ...(acc[index] ?? {}), [label]: value };
});
return acc;
}, []);
console.log(res);
Since your index comes from the inner loop, you'll be replacing the values at actualData[index] each outer loop iteration, that's why you only end up with the last.
Try this reduce operation instead...
const inputData = {"dimensions":[{"id":"dimension_re","label":"Region","values":["East","East","West","SouthWest","South","NorthEast"]},{"id":"dimension_cnt","label":"County","values":["London","Italy","Germany","US","Russia","India"]},{"id":"measure_sales","label":"Sales","values":[100,156,432,462,25,100]},{"id":"measure_qty","label":"Quantity","values":[85,34,153,434,52,43]},{"id":"measure_profit","label":"Profit","values":[123,45,421,465,451,56]}]};
// Find the max number of `values`
const maxLength = Math.max(...inputData.dimensions.map(({ values }) => values.length));
const actualData = inputData.dimensions.reduce(
(arr, { label, values }) =>
arr.map((obj, i) => ({ ...obj, [label]: values[i] })),
Array.from(
{
length: maxLength,
},
() => ({}) // creates an array of empty objects
)
);
console.log(actualData);
.as-console-wrapper { max-height: 100% !important; }

Transforming data between 2 structures

I need to calculate a score based on departments in groups. To simplify that I used only 1 group as an example.
I'm receiving this data structure:
const data = [{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 80,
"count": 1,
"department": "Engineering",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 40,
"count": 1,
"department": "Executive",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 40,
"count": 1,
"department": "OOO Boost",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 60,
"count": 1,
"department": "Engineering",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 100,
"count": 2,
"department": "Supporting Department",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 20,
"count": 1,
"department": "Designers",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 20,
"count": 1,
"department": "Executive",
"group": "Group one"
}
];
and I need to create a data structure for a heatmap:
const heatmapData = [
{
row: 'Group one',
columns: [
{
name: 'Engineering',
averageScore: 70, // (80[score] * 1[count] + 60[score] * 1[count]) / 2[sum of count -> 1+1]
},
{
name: 'Supporting Department',
averageScore: 100, // 100[score] * 2[count] / 2[sum of count -> 2]
},
.... next unique departments
]
}
]
I'm a bit stacked with some simple solution of grouping data with calculation. Could you help me please? Thank you
You can group your data based on the group and for each group, group it on department and sum up score and count. Once you have this object, you can calculate average for each department.
const data = [{ "id": "cklt7ln1k0922o0sabjkk74m9", "score": 80, "count": 1, "department": "Engineering", "group": "Group one" }, { "id": "cklt7ln1k0922o0sabjkk74m9", "score": 40, "count": 1, "department": "Executive", "group": "Group one" }, { "id": "cklt7ln1k0922o0sabjkk74m9", "score": 40, "count": 1, "department": "OOO Boost", "group": "Group one" }, { "id": "cklt7ln1k0922o0sabjkk74m9", "score": 60, "count": 1, "department": "Engineering", "group": "Group one" }, { "id": "cklt7ln1k0922o0sabjkk74m9", "score": 100, "count": 2, "department": "Supporting Department", "group": "Group one" }, { "id": "cklt7ln1k0922o0sabjkk74m9", "score": 20, "count": 1, "department": "Designers", "group": "Group one" }, { "id": "cklt7ln1k0922o0sabjkk74m9", "score": 20, "count": 1, "department": "Executive", "group": "Group one" } ],
grouped = data.reduce((r, {group, department, score, count}) => {
r[group] = r[group] || {};
r[group][department] = r[group][department] || {department, totalScore: 0, count: 0 };
r[group][department].totalScore += score * count;
r[group][department].count += count;
return r;
},{}),
result = Object.keys(grouped).map(k => {
const columns = Object.values(grouped[k]).map(d => ({
department: d.department,
averageScore: d.totalScore/d.count
}));
return {row: k, columns };
})
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can group items into the dictionary:
let group = data.reduce((acc, val) => {
if(!acc[val.group]) acc[val.group] = {};
if(!acc[val.group][val.department]) acc[val.group][val.department] = {sum:0, count: 0, averageScore: 0};
acc[val.group][val.department] = {
sum: acc[val.group][val.department].sum+val.score,
count: acc[val.group][val.department].count + 1,
averageScore: (acc[val.group][val.department].sum+val.score) / (acc[val.group][val.department].count + 1)
}
return acc
}, {});
then map it to requested format:
Object.keys(group).map(row => {
return {
row: row,
columns: Object.keys(group[row]).map(col => {return {name: col, averageScore: group[row][col].averageScore}})
}
})
Allow me to explain how it works.. so we know that objects are pointers more than they are values.. I used that principle throughout the solution. I have the array to return(where I push stuff), then I had 2 Objects to store values(and editing those Objects edited the "values" in the array), so in the array you pass in, I basically add values to these objects and link them in the array(the format you desire)
UPDATED AGAIN.. I applied the logic of your calculation examples >:D
function group(array){
let arr=[]; let groups={}; let departments={}
array.forEach(a=>{
if(!groups[a.group]){ //setting each group
groups[a.group]={row:a.group,columns:[]}
arr.push(groups[a.group])
}
if(!departments[a.department]){ //setting each department
departments[a.department]=[{name:a.department,averageScore:a.score*a.count},a.count] //score * count and count(because of your calculation examples)
groups[a.group].columns.push(departments[a.department][0])
}
else{ //adding department when they already exist
departments[a.department][1]+=a.count //count
departments[a.department][0].averageScore+=a.score*a.count //score * count
//(because of your calculation examples)
}
})
Object.keys(departments).forEach(a=>departments[a][0].averageScore/=departments[a][1])
return arr
}
console.log(group(data))
<script>
//to not take out unnecesary space in the answer
window.data = [{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 80,
"count": 1,
"department": "Engineering",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 40,
"count": 1,
"department": "Executive",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 40,
"count": 1,
"department": "OOO Boost",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 60,
"count": 1,
"department": "Engineering",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 100,
"count": 2,
"department": "Supporting Department",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 20,
"count": 1,
"department": "Designers",
"group": "Group one"
},
{
"id": "cklt7ln1k0922o0sabjkk74m9",
"score": 20,
"count": 1,
"department": "Executive",
"group": "Group one"
}
];
</script>

merge same properties of array of objects and create array of object within that array of object for different properties

This is my array
"status": "Success",
"results": 54,
"orders": [
{
"order_id": 261,
"image": "test1.png",
"productName": "test1",
"price": 2,
"quantity": 2,
"purAmt": 34,
"created_at": "2020-07-27T06:29:32.000Z",
"shopName": "abc"
},
{
"order_id": 261,
"image": "test2.png",
"productName": "test2",
"price": 30,
"quantity": 1,
"purAmt": 34,
"created_at": "2020-07-27T06:29:32.000Z",
"shopName": "abc"
},
]
I want to combine properties with same name and create array of object within a object.
expected output
"status": "Success",
"results": 54,
"orders": [
{
"order_id": 261,
"purAmt": 34,
"created_at": "2020-07-27T06:29:32.000Z",
"shopName": "abc"
products : [
{
"productName": "test1",
"price": 2,
"quantity": 2,
"image": "test1.png",
},
{
"productName": "test2",
"price": 5,
"quantity": 3,
"image": "test2.png",
},
}
]
I would be better if I get the solution using reduce function and without any libraries like lodash
My attempt of doing it using reduce fcn
var result = Object.values(
data.reduce(
(acc, { productName, price, image, quantity, ...rest }) => {
acc[rest.orderID] = acc[rest.orderID] || { ...rest, products: [] };
acc[rest.orderID].products.push({
productName,
price,
image,
quantity,
});
return acc;
},
{},
),
);
This should work for you
orders = [
{
order_id: 261,
image: "test1.png",
productName: "test1",
price: 2,
quantity: 2,
purAmt: 34,
created_at: "2020-07-27T06:29:32.000Z",
shopName: "abc",
},
{
order_id: 261,
image: "test2.png",
productName: "test2",
price: 30,
quantity: 1,
purAmt: 34,
created_at: "2020-07-27T06:29:32.000Z",
shopName: "abc",
},
];
const res = Object.values(
orders.reduce((acc, { image, productName, price, quantity, ...rest }) => {
if (!acc[rest.order_id]) {
acc[rest.order_id] = { products: [] };
}
acc[rest.order_id] = {
...acc[rest.order_id],
...rest,
products: [
...acc[rest.order_id].products,
{
image,
productName,
price,
quantity,
},
],
};
return acc;
}, {})
);
console.log(res);
Extended Version: I extend it, so there are now different order_id's considered. For example I add a id 262 which creates another element in orders-array.
let data = { "status": "Success",
"results": 54,
"orders": [
{
"order_id": 261,
"image": "test1.png",
"productName": "test1",
"price": 2,
"quantity": 2,
"purAmt": 34,
"created_at": "2020-07-27T06:29:32.000Z",
"shopName": "abc"
},
{
"order_id": 262,
"image": "test3.png",
"productName": "test3",
"price": 70,
"quantity": 5,
"purAmt": 17,
"created_at": "2020-07-19T06:29:32.000Z",
"shopName": "xyz"
},
{
"order_id": 261,
"image": "test2.png",
"productName": "test2",
"price": 30,
"quantity": 1,
"purAmt": 34,
"created_at": "2020-07-27T06:29:32.000Z",
"shopName": "abc"
},
]};
let orderIndex =[];
let orders = [];
data.orders.forEach(orderObj => {
let order;
let index = orderIndex.indexOf(orderObj.order_id);
if (index == -1) {
orderIndex.push(orderObj.order_id);
order = {};
orders.push(order);
['order_id', 'purAmt', 'created_at', 'shopName'].forEach( elem => {
order[elem] = orderObj[elem];
order.products = [];
});
} else {
order = orders[index];
}
let product = {};
['productName', 'price', 'image', 'quantity'].forEach( elem => {
product[elem] = orderObj[elem];
});
order.products.push(product);
});
let result = {
status: data.status,
results: data.results,
orders: orders
}
console.log(result);

Merge elements in array of objects based on property

I have an array of objects. Each object has an amount and value property. If an object has the same amount value I would like to add that value to that object.
Here's an example array:
const array = [
{
"key": 1,
"amount": 11,
"value": "were"
},
{
"key": 2,
"amount": 6,
"value": "locomotives"
},
{
"key": 3,
"amount": 5,
"value": "They"
},
{
"key": 4,
"amount": 5,
"value": "with"
},
{
"key": 5,
"amount": 4,
"value": "used"
}
]
I would like to transform this to resemble this:
const array = [
{
"key": 1,
"amount": 11,
"value": "were"
},
{
"key": 2,
"amount": 6,
"value": "locomotives"
},
{
"key": 3,
"amount": 5,
"value": "They, width"
},
{
"key": 5,
"amount": 4,
"value": "used"
}
]
I've tried reduce and map but I can't seem to get it to join,
I think should work with .reduce():
const array = [
{
"key": 1,
"amount": 11,
"value": "were"
},
{
"key": 2,
"amount": 6,
"value": "locomotives"
},
{
"key": 3,
"amount": 5,
"value": "They"
},
{
"key": 4,
"amount": 5,
"value": "with"
},
{
"key": 5,
"amount": 4,
"value": "used"
}
];
const result = array.reduce((a, c) => {
const found = a.find(e => e.amount === c.amount);
if (found) found.value = `${found.value}, ${c.value}`;
return found ? a : a.concat(c);
}, []);
console.log(result);
I hope that helps!
You can use .reduce() with an ES6 Map by indexing by the amount value. If an object's amount value already exists within the map, you can update its value to include the current objects value. If the amount value isn't in the map, you can set it as a key and the current object as the value. Lastly, you can use Array.from() to get an array of object values from the iterator returned by .values()
const array = [ { "key": 1, "amount": 11, "value": "were" }, { "key": 2, "amount": 6, "value": "locomotives" }, { "key": 3, "amount": 5, "value": "They" }, { "key": 4, "amount": 5, "value": "with" }, { "key": 5, "amount": 4, "value": "used" } ];
const res = Array.from(array.reduce((m, o) => {
const curr = m.get(o.amount);
return m.set(o.amount, curr && {...curr, value: `${curr.value}, ${o.value}`} || o);
}, new Map).values());
console.log(res);
mine..
const array1 =
[ { key: 1, amount: 11, value: "were" }
, { key: 2, amount: 6, value: "locomotives" }
, { key: 3, amount: 5, value: "They" }
, { key: 4, amount: 5, value: "with" }
, { key: 5, amount: 4, value: "used" }
]
const array2 = array1.reduce((a,c)=>
{
let same = a.find(e=>e.amount===c.amount)
if (same) same.value += ', '+c.value
else a.push(c)
return a
},[])
console.log( array2 )
In each iteration of reduce method, we can add value if there is an already added value:
const result = array.reduce((a, c) => {
a[c.amount] = a[c.amount] || c;
if ((Object.keys(a).includes(c.amount.toString())) && (a[c.amount].value!= c.value))
a[c.amount].value += ', ' + c.value;
return a;
}, {});
An example:
const array = [
{
"key": 1,
"amount": 11,
"value": "were"
},
{
"key": 2,
"amount": 6,
"value": "locomotives"
},
{
"key": 3,
"amount": 5,
"value": "They"
},
{
"key": 4,
"amount": 5,
"value": "with"
},
{
"key": 5,
"amount": 4,
"value": "used"
}
];
const result = array.reduce((a, c) => {
a[c.amount] = a[c.amount] || c;
if ((Object.keys(a).includes(c.amount.toString())) && (a[c.amount].value!= c.value))
a[c.amount].value += ', ' + c.value;
return a;
}, {});
console.log(result);
Use forEach loop and build an object. If the amount key already exist then append the value string.
const update = data => {
const res = {};
data.forEach(item => {
res[item.amount] =
item.amount in res
? {
...res[item.amount],
value: `${res[item.amount].value}, ${item.value}`
}
: { ...item };
});
return Object.values(res);
};
const array = [
{
key: 1,
amount: 11,
value: "were"
},
{
key: 2,
amount: 6,
value: "locomotives"
},
{
key: 3,
amount: 5,
value: "They"
},
{
key: 4,
amount: 5,
value: "with"
},
{
key: 5,
amount: 4,
value: "used"
}
];
console.log(update(array));

Adding position/order in array of objects

I have an array of objects like below:
[{
"id": 1,
"Size": 90,
"Maturity": 24,
},
{
"id": 2,
"Size": 85,
"Maturity": 22,
},
{
"id": 3,
"Size": 80,
"Maturity": 20,
}]
I need to this Array on basis of different property value sorting (eg: Maturity) and also add a column order which has the ascending order/rank.
Eg:
[{
"id": 1,
"Size": 90,
"Maturity": 22,
"Order": 2
},
{
"id": 2,
"Size": 85,
"Maturity": 25,
"Order": 3
},
{
"id": 3,
"Size": 80,
"Maturity": 20,
"Order": 1
}]
const arr = [{
"id": 1,
"Size": 90,
"Maturity": 24,
},
{
"id": 2,
"Size": 85,
"Maturity": 22,
},
{
"id": 3,
"Size": 80,
"Maturity": 20,
}];
arr
.map((item,index) => ({ ...item, Order: index + 1 }))
.sort((a, b) => b.Maturity - a.Maturity)
Sort the array with sort and then add prop to each object with respect to the index they are sorted on with forEach:
var inp = [{
id: 1,
Size: 90,
Maturity: 24,
},
{
id: 2,
Size: 85,
Maturity: 22,
},
{
id: 3,
Size: 80,
Maturity: 20,
}]
// Sort
inp.sort(function(a, b){
return a.Maturity == b.Maturity ? 0 : +(a.Maturity > b.Maturity) || -1;
});
// add prop
inp.forEach(function(row, index) {
row.index = index + 1;
});
console.log(inp)
var objs = [
{
"id": 1,
"Size": 90,
"Maturity": 24,
},
{
"id": 2,
"Size": 85,
"Maturity": 22,
},
{
"id": 3,
"Size": 80,
"Maturity": 20,
}];
function compare(a,b) {
if (a.Size < b.Size)
return -1;
if (a.Size > b.Size)
return 1;
return 0;
}
objs.sort(compare);
for (var i = 0; i < objs.length; i++) {
objs[i].Order = i+1;
}
console.log(objs);

Categories