Create an object from an array of objects with custom key values - javascript

now I know there are a few questions that are similar like this question and this question but neither answer my question.
Okay so...
Say I have an api call and I get a response like this
[
{
amount: 23,
bill: 47,
otherData: null,
title: 'cool title'
},
{
amount: 223,
bill: 427,
otherData: null,
title: 'cooler title'
},
{
amount: 2313,
bill: 437,
otherData: null,
title: 'super cool title'
},
{
amount: 123,
bill: 147,
otherData: null,
title: 'coolest title'
}
]
is there a way I can create a new object from this array and have custom key names using a property in the object?? so the desired output is..
{
coolTitle: {
amount: 23,
bill: 47,
otherData: null,
title: 'cool title'
},
coolerTitle: {
amount: 223,
bill: 427,
otherData: null,
title: 'cooler title'
},
superCoolTitle: {
amount: 2313,
bill: 437,
otherData: null,
title: 'super cool title'
},
coolestTitle: {
amount: 123,
bill: 147,
otherData: null,
title: 'coolest title'
}
}
now I know I can convert an array of objects into an object like so..
var result = {};
for (var i=0; i<array.length; i++) {
result[array[i].key] = array[i].value;
}
but I have no Idea how I could get the title from each object, camelCase it and then create the custom key and the object
I'm not even sure if something like this is possible, any help would be appreciated
Thanks

To get the property name, extract the title and replace its space-characters with the upper-case character. Then, it's as simple as reduce-ing into an object:
const input=[{amount:23,bill:47,otherData:null,title:'cool title'},{amount:223,bill:427,otherData:null,title:'cooler title'},{amount:2313,bill:437,otherData:null,title:'super cool title'},{amount:123,bill:147,otherData:null,title:'coolest title'}]
console.log(
input.reduce((a, item) => {
const { title } = item;
const camel = title.replace(/ ./g, chars => chars[1].toUpperCase());
a[camel] = item;
return a;
}, {})
);

Use map & reduce, The reduce method will return the new object with custom key. Inside the reduce method use map. This will create an array of the value of title like ['cool','title']. Inside the same map method create a string to
convert the first character of the word to upperCase and join to join all the words
let oldArr = [{
amount: 23,
bill: 47,
otherData: null,
title: 'cool title'
},
{
amount: 223,
bill: 427,
otherData: null,
title: 'cooler title'
},
{
amount: 2313,
bill: 437,
otherData: null,
title: 'super cool title'
},
{
amount: 123,
bill: 147,
otherData: null,
title: 'coolest title'
}
]
let newArray = oldArr.reduce(function(acc, curr) {
let crtTitle = curr.title.split(' ').map(function(item, index) {
if (index !== 0) {
return item.substring(0, 1).toUpperCase() + item.substring(1, item.length);
} else {
return item;
}
}).join('');
acc[crtTitle] = curr;
return acc
}, {});
console.log(newArray)

Related

How can I access this property in my dictionary in js?

I thought I understood how to loop through a dictionary, but my loop is wrong. I try to access the name of each sub item but my code does not work.
Here is what I did:
list = [
{
title: 'Groceries',
items: [
{
id: 4,
title: 'Food',
cost: 540 ,
},
{
id: 5,
title: 'Hygiene',
cost: 235,
},
{
id: 6,
title: 'Other',
cost: 20,
},
],
}];
function calculateCost(){
let total = 0;
Object.keys(list).forEach((k) => { for (i in k.items) { total += i.data; } });
console.log(total);
return total;
}
Your list is an array includes 1 object and this object has two properties title and items the items here is an array of objects each one of these objects has property cost so to calculate the total cost you need to loop through items array, here is how you do it:
let list = [
{
title: 'Groceries',
items: [
{
id: 4,
title: 'Food',
cost: 540 ,
},
{
id: 5,
title: 'Hygiene',
cost: 235,
},
{
id: 6,
title: 'Other',
cost: 20,
},
],
}];
function calculateCost(){
let total = 0;
list[0].items.forEach(el => {
total += el.cost;
})
console.log(total)
return total;
}
calculateCost();
Your list is an Array, not an Object.
Instead of Object.keys() use Array.prototype.reduce:
const calculateCost = (arr) => arr.reduce((tot, ob) =>
ob.items.reduce((sum, item) => sum + item.cost, tot), 0);
const list = [
{
title: 'Groceries',
items: [
{id: 4, title: 'Food', cost: 10},
{id: 5, title: 'Hygiene', cost: 20},
{id: 6, title: 'Other', cost: 30}
]
}, {
title: 'Other',
items: [
{id: 8, title: 'Scuba gear', cost: 39}
],
}
];
console.log(calculateCost(list)); // 99
Expanding on #Roko's and #mmh4all's answers, the following code adds several verification statements to handle cases where a deeply nested property in your data is not what you expect it to be.
const calculateCost = (orders) => {
let listOfCosts = [];
// For each 'order' object in the 'orders' array,
// add the value of the 'cost' property of each item
// in the order to 'listOfCosts' array.
orders.forEach(order => {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
if (!Array.isArray(order.items)) { return; }
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/parseFloat
const orderCostArr = order.items.map(item =>
isNaN(item.cost) ? 0 : parseFloat(item.cost, 10));
if (orderCostArr.length === 0) { return; }
// Concatenate 'orderCostArr' to the 'listOfCosts' array
//listOfCosts = listOfCosts.concat(orderCostArry);
// Alternate approach is to use the spread syntax (...) to
// push the items in the array returned by 'order.items.map()'
// into the 'listOfCosts' array.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
listOfCosts.push(...orderCostArr);
});
// Use the 'reduce' method on the 'listOfCosts' array
// to get the total cost.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
const totalCost = listOfCosts.reduce(
(accumulator, currentValue) => accumulator + currentValue, 0);
return totalCost;
};
const list = [
{
title: 'Groceries',
items: [
{ id: 4, title: 'Food', cost: 10 },
{ id: 3, title: 'Baked goods', cost: 20 },
{ id: 5, title: 'Hygiene', cost: 0 },
{ id: 6, title: 'Other' }
]
}, {
title: 'Gear',
items: {},
}, {
title: 'Accessories',
items: [],
}, {
title: 'Bags',
}, {
title: 'Other',
items: [
{ id: 10, title: 'Scuba gear', cost: "5" },
{ id: 8, title: 'Scuba gear', cost: "err" },
{ id: 9, title: 'Scuba gear', cost: 59 }
],
}
];
console.log(calculateCost(list)); // 94

Amend an object in an array

I need to amend the amount to add £ before the number and change it to two decimals i.e. £9.00
const data = [
{
title: 'Todo',
amount: 9.99,
},
{
title: 'In-Progress',
amount: 4,
},
{
title: 'Completed',
amount: 10,
},
{
title: 'Short',
amount: 15.48,
},
];
I can't work out how?
You could do that, or you could use the Intl.NumberFormat to change it to currency on-the-fly. That way you get to keep your data neutral, and let the browser/JS handle the transformation only when it's needed.
const data=[{title:"Todo",amount:9.99},{title:"In-Progress",amount:4},{title:"Completed",amount:10},{title:"Short",amount:15.48}];
const style = { style: 'currency', currency: 'GBP' };
for (const obj of data) {
const currency = new Intl.NumberFormat('en-GB', style).format(obj.amount);
console.log(currency);
}
If you still want to change the data this method is still preferable. map over the data, destructure the amount from the object, and return a new object with the transformed amount.
const data=[{title:"Todo",amount:9.99},{title:"In-Progress",amount:4},{title:"Completed",amount:10},{title:"Short",amount:15.48}];
const style = { style: 'currency', currency: 'GBP' };
function toGBP(amount) {
return new Intl.NumberFormat('en-GB', style).format(amount);
}
const out = data.map(obj => {
const { amount, ...rest } = obj;
return { ...rest, amount: toGBP(amount) };
});
console.log(out);
Additional documentation
Rest parameters
Spread syntax
Just map through it and use .toFixed(2) for the decimal.
const data = [
{
title: 'Todo',
amount: 9.99,
},
{
title: 'In-Progress',
amount: 4,
},
{
title: 'Completed',
amount: 10,
},
{
title: 'Short',
amount: 15.48,
},
];
const mapped = data.map((n) => { return {title: n.title, amount: `£${n.amount.toFixed(2)}`}});
console.log(mapped);
You can use Number#toFixed(2) and Array#map() methods as follows:
const
data = [ { title: 'Todo', amount: 9.99, }, { title: 'In-Progress', amount: 4, }, { title: 'Completed', amount: 10, }, { title: 'Short', amount: 15.48 } ],
out = data.map(({title,amount}) => ({title,amount:`£${amount.toFixed(2)}`}));
console.log( out );

How to loop over a an array that is already inside a map function without duplicating the contents

I have the following code with the following arrays. I want to loop through both of them and pull out some data, and put them inside a final array. I am able to do that, but the contents are duplicated. I tried reading about reduce but don't quite understand it, and am not sure if it's the right solution. I have also setup a jsfiddle
https://jsfiddle.net/anders_kitson/Lcqn6fgd/
var lineItems = [{
id: 'li_1HyhAZHk5l44uIELgsMWqHqB',
object: 'item',
amount_subtotal: 7500,
amount_total: 7500,
currency: 'cad',
description: 'The Spencer',
price: [Object],
quantity: 1
},
{
id: 'li_1HyhAZHk5l44uIELeNUsiZPu',
object: 'item',
amount_subtotal: 7500,
amount_total: 7500,
currency: 'cad',
description: 'The Gertie',
price: [Object],
quantity: 1
}
]
var arr = [{
id: 'prod_IS1wY1JvSv2CJg',
object: 'product',
active: true,
attributes: [],
created: 1606248785,
description: 'Shelf Set',
images: [
'https://files.stripe.com/links/fl_test_raNEqk9ZhzX3WdQsnvXX4gFq'
],
livemode: false,
metadata: {},
name: 'The Spencer',
statement_descriptor: null,
type: 'service',
unit_label: null,
updated: 1606248785
},
{
id: 'prod_IS299dMnC13Ezo',
object: 'product',
active: true,
attributes: [],
created: 1606249543,
description: 'Shelf Set',
images: [
'https://files.stripe.com/links/fl_test_QPbrP76uNn4QadgcUwUnkmbe'
],
livemode: false,
metadata: {},
name: 'The Gertie',
statement_descriptor: null,
type: 'service',
unit_label: null,
updated: 1606249543
}
];
let productArr = [];
arr.map((item) => {
lineItems.map((line) => {
productArr.push({
image: item.images[0],
name: item.name,
price: line.amount_total,
});
});
});
console.log(productArr);
This is the output I get where you can see the array repeats the values, and I know I have coded it this way I just don't know how to fix it.
[{
image: "https://files.stripe.com/links/fl_test_raNEqk9ZhzX3WdQsnvXX4gFq",
name: "The Spencer",
price: 7500
}, {
image: "https://files.stripe.com/links/fl_test_raNEqk9ZhzX3WdQsnvXX4gFq",
name: "The Spencer",
price: 7500
}, {
image: "https://files.stripe.com/links/fl_test_QPbrP76uNn4QadgcUwUnkmbe",
name: "The Gertie",
price: 7500
}, {
image: "https://files.stripe.com/links/fl_test_QPbrP76uNn4QadgcUwUnkmbe",
name: "The Gertie",
price: 7500
}]
To Be more clear this is the output that I want
[{
image: "https://files.stripe.com/links/fl_test_raNEqk9ZhzX3WdQsnvXX4gFq",
name: "The Spencer",
price: 7500
}, {
image: "https://files.stripe.com/links/fl_test_QPbrP76uNn4QadgcUwUnkmbe",
name: "The Gertie",
price: 7500
},
]
I have tried the suggestion in the comments with the following
let b
arr.map((item) => {
b = lineItems.map((line) => {
return {
image: item.images[0],
name: item.name,
price: line.amount_total,
};
});
});
but it returns the same ones twice
[{
image: "https://files.stripe.com/links/fl_test_QPbrP76uNn4QadgcUwUnkmbe",
name: "The Gertie",
price: 7500
}, {
image: "https://files.stripe.com/links/fl_test_QPbrP76uNn4QadgcUwUnkmbe",
name: "The Gertie",
price: 7500
}]
Although not expressed directly in your question, it seems you're looking to do a join in javascript. The only things I see relating the two are 'name' in products and 'description' in the line items. So do a loop join on that.
Here's some sample code using your example but stripped down only to what's relevant:
var lineItems = [
{ amount_total: 7500, description: 'The Spencer' },
{ amount_total: 7500, description: 'The Gertie' }
]
var arr = [
{ images: ['Spencer Image 1'], name: 'The Spencer' },
{ images: ['Gertie Image 1'], name: 'The Gertie' }
]
let joined = arr
.flatMap(a => lineItems.map(li => ({a, li})))
.filter(obj => obj.a.name == obj.li.description)
.map(obj => ({
image: obj.a.images[0],
name: obj.a.name,
price: obj.li.amount_total
}));
console.log(joined);
Being a loop join, it may not be that efficient. To do a hash join is a little more involved. You can look through the source code of my developing project
fluent-data, or it might even be useful to you to use it directly if you can follow the documentation.
You can use a single map call and reference your second lineItems array either by index, if you know that the two arrays are the same length and order
const output = arr.map((o, i) => ({
name: o.name,
image: o.images[0],
price: lineItems[i].amount_total}
));
or by using find() to retrieve the relevant object.
const outputUsingFind = arr.map(o => {
const lineItem = lineItems.find(item => item.description === o.name);
// ** add lineItem valid check here **
return {
name: o.name,
image: o.images[0],
price: lineItem.amount_total};
});
var lineItems = [{amount_subtotal: 7500,amount_total: 700,description: 'The Spencer',},{amount_subtotal: 7500,amount_total: 500,description: 'The Gertie',}];
var arr = [{images: ['spencer image'],name: 'The Spencer',},{images: ['gertie image'],name: 'The Gertie'}];
// since your arrays are ordered the same you can access the second object using
// the index passed from map.
const output = arr.map((o, i) => ({
name: o.name,
image: o.images[0],
price: lineItems[i].amount_total}
));
console.log(output);
// if the two arrays are not in the same order you can use find() to retrieve
// the second object by property (you'll need to check
const outputUsingFind = arr.map(o => {
const lineItem = lineItems.find(item => item.description === o.name);
// ** add lineItem valid check here **
return {
name: o.name,
image: o.images[0],
price: lineItem.amount_total};
});
console.log(outputUsingFind);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to groupBy array of objects based on properties in vanilla javascript

How do you groupBy array of objects based on specific properties in vanilla javascript? For example this given:
const products = [
{
category: "Sporting Goods",
price: "$49.99",
stocked: true,
name: "Football"
},
{
category: "Sporting Goods",
price: "$9.99",
stocked: true,
name: "Baseball"
},
{
category: "Sporting Goods",
price: "$29.99",
stocked: false,
name: "Basketball"
},
{
category: "Electronics",
price: "$99.99",
stocked: true,
name: "iPod Touch"
},
{
category: "Electronics",
price: "$399.99",
stocked: false,
name: "iPhone 5"
},
{ category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7" }
];
i want to run a reduce function that would result to a new array of objects like this:
Intended Output:
const categorize = [
{
category:"Sporting Goods",
products: [
{
name:"Football",
price: "$49.99",
stocked: true
},
{
name:"Baseball",
price: "$9.99",
stocked: true
},
{
name:"Basketball",
price: "$29.99",
stocked: true
}
]
},
{
category: "Electronics",
products: [
{
name: "iPod Touch",
price: "$99.99",
stocked: true
},
{
name: "iPhone 5",
price: "$399.99",
stocked: false
},
{
name: "Nexus 7",
price: "$199.99",
stocked: true
}
]
}
]
i based my solution from the tutorial here: https://www.consolelog.io/group-by-in-javascript/ using the reduce function.
Here's my code:
const groupBy = (arr,prop)=>{
return arr.reduce((groups,item)=>{
let val = item[prop];
groups[val] = groups[val]||[];
groups[val].push(item);
return groups
},{});
}
const categorize = groupBy(products,'category');
console.log(categorize);
/* returns an Object like
Object {Sporting Goods: Array[3], Electronics: Array[3]}
however it's not the intended output.
*/
I tried to return Object.values(obj) or Object.entries(obj) inside the groupBy function but it just returns an array of 2 arrays like [Array[3],Array[3]] and if i set the initial value (2nd parameter of reduce) to empty [] instead of {}, the output is just an empty array. Need help, thanks!
Because you want an array containing objects (rather than just an array of plain values), create a { category, products: [] } object if it doesn't exist in the accumulator:
const products=[{category:"Sporting Goods",price:"$49.99",stocked:!0,name:"Football"},{category:"Sporting Goods",price:"$9.99",stocked:!0,name:"Baseball"},{category:"Sporting Goods",price:"$29.99",stocked:!1,name:"Basketball"},{category:"Electronics",price:"$99.99",stocked:!0,name:"iPod Touch"},{category:"Electronics",price:"$399.99",stocked:!1,name:"iPhone 5"},{category:"Electronics",price:"$199.99",stocked:!0,name:"Nexus 7"}];
const output = Object.values(
products.reduce((a, { category, ...item }) => {
if (!a[category]) {
a[category] = { category, products: [] };
}
a[category].products.push(item);
return a;
}, {})
);
console.log(output);
function (){
var map = {};
products.forEach((p) => {
if (map[p.category]) {
var c = p.category;
delete p.category;
map[c].push(p);
} else {
var c = p.category;
delete p.category;
map[c]=[c]
}
});
Object.keys(map).forEach((m) => {
ans.push({category:m, products: map[m]})
})
}
you can collect in one go products and map them.
Then make your resultinng array

Group and merge in nested object using Lodash

I have an object data as follows:
[
{
name: "Green Tea Brownie",
price: 60,
amount: 10,
seller: {
seller_id: 124,
seller_name: "Merry Shop"
}
},
{
name: "Cocoa Chiffon",
price: 20,
amount: 50,
seller: {
seller_id: 124,
seller_name: "Merry Shop"
}
},
{
name: "Milky Donut",
price: 40,
amount: 100
seller: {
seller_id: 421,
seller_name: "Sweet Bakery"
}
}
]
So I want to group data by "seller_id" and merge top level data assigns to object name "orders", just look like as following:
[
{
seller_id: 124,
seller_name: "Merry Shop",
orders: [
{
name: "Green Tea Brownie",
price: 60,
amount: 10
},
{
name: "Cocoa Chiffon",
price: 20,
amount: 50
}
]
},
{
seller_id: 421,
seller_name: "Sweet Bakery",
orders: [
{
name: "Milky Donut",
price: 40,
amount: 100
}
]
}
]
I tried to solve this problem several hours ago. Can anyone solve this case?
Thank you
You can use _.groupBy() and then _.map() the groups to requested format:
const { flow, partialRight: pr, groupBy, map, first, omit } = _
const fn = flow(
pr(groupBy, 'seller.seller_id'),
pr(map, group => ({
...first(group).seller,
orders: map(group, pr(omit, 'seller'))
}))
)
const data = [{"name":"Green Tea Brownie","price":60,"amount":10,"seller":{"seller_id":124,"seller_name":"Merry Shop"}},{"name":"Cocoa Chiffon","price":20,"amount":50,"seller":{"seller_id":124,"seller_name":"Merry Shop"}},{"name":"Milky Donut","price":40,"amount":100,"seller":{"seller_id":421,"seller_name":"Sweet Bakery"}}]
const result = fn(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
If you like to do with plain JS you can use reduce
const data = [{"name":"Green Tea Brownie","price":60,"amount":10,"seller":{"seller_id":124,"seller_name":"Merry Shop"}},{"name":"Cocoa Chiffon","price":20,"amount":50,"seller":{"seller_id":124,"seller_name":"Merry Shop"}},{"name":"Milky Donut","price":40,"amount":100,"seller":{"seller_id":421,"seller_name":"Sweet Bakery"}}]
const output = data.reduce((op,{name,price,amount,seller})=>{
if(op[seller['seller_id']]){
op[seller['seller_id']]['orders'].push({name,price,amount})
}
else {
op[seller['seller_id']] = {
seller_name: seller.seller_name,
seller_id: seller.seller_id,
orders: [{name,price,amount}]
}
}
return op;
},{})
console.log(Object.values(output))
Here is a pure JS solution using reduce:
const orders = [{
name: "Green Tea Brownie",
price: 60,
amount: 10,
seller: {
seller_id: 124,
seller_name: "Merry Shop"
}
}, {
name: "Cocoa Chiffon",
price: 20,
amount: 50,
seller: {
seller_id: 124,
seller_name: "Merry Shop"
}
}, {
name: "Milky Donut",
price: 40,
amount: 100,
seller: {
seller_id: 421,
seller_name: "Sweet Bakery"
}
}];
const ordersBySeller = Object.values(orders.reduce((accum, { name, price, amount, seller: { seller_id, seller_name } }) => {
const sellerOrders = accum[seller_id] ? accum[seller_id].orders || [] : [];
accum[seller_id] = { seller_id, seller_name, orders: [...sellerOrders, { name, price, amount } ] };
return accum;
}, {}));
console.log(ordersBySeller);

Categories

Resources