I have an array of objects as mentioned below.
const inputArray =[
{
name: "Energy",
quantity: [
{
qval: "100 ",
unit: "unit1"
},
{
qval: "200 ",
unit: "unit2"
}
],
},
{
name: "Fat",
quantity: [
{
qval: "300",
unit: "unit3"
}
],
},
]
I'm trying to restructure this array using the following code and I got something as mentioned below
const outputArray = inputArray.map(function(item,i) {
return {
name: item.name,
amount: (item.quantity[0] &&
item.quantity[0].qval+item.quantity[0].unit)+'|'+ (item.quantity[1] && item.quantity[1].qval+item.quantity[1].unit),
};
});
And here is the output I got
[
{name: "Energy", amount: "100 unit1|200 unit2"}
{name: "Fat", amount: "300unit3|undefined"}
]
Since I'm new to this, I don't think this is a good method, please suggest any simpler neat code.
I'm expecting
[
{name: "Energy", amount: "100 unit1|200 unit2"}
{name: "Fat", amount: "300unit3"}
]
Also I need to remove 'undefined' if that value doesn't exist.
Please suggest.
there you go
inputArray.map(item => ({
name: item.name,
amount: item.quantity.reduce((accumulator, currentValue) => (accumulator+currentValue.qval+currentValue.unit+"|"),"").slice(0, -1)
}))
Here's a pretty simple approach, using a map for the outer list and another one for the quantities for each:
const combine = arr => arr.map(({name, quantity}) => ({
name,
amount: quantity.map(({qval, unit}) => `${qval}${unit}`).join('|')
}))
const inputArray = [{name: "Energy", quantity: [{qval: "100 ", unit: "unit1"}, {qval: "200 ", unit: "unit2"}]}, {name: "Fat", quantity: [{qval: "300", unit: "unit3"}]}]
console.log(combine(inputArray))
The biggest advantage of this approach over yours is that it works for any number of quantities per item. There is no special-case code for the first or second one.
You can add ternary conditions inside your map function to account for variables that might not be declared. For example:
const inputArray =[
{
name: "Energy",
quantity: [
{
qval: "100 ",
unit: "unit1"
},
{
qval: "200 ",
unit: "unit2"
}
],
},
{
name: "Fat",
quantity: [
{
qval: "300",
unit: "unit3"
}
],
},
]
const outputArray = inputArray.map(function(item,i) {
return {
name: item.name,
amount: `${item.quantity[0] ?
item.quantity[0].qval+item.quantity[0].unit : ''}${item.quantity[1] ? `|${item.quantity[1].qval+item.quantity[1].unit}` : ''}`,
};
})
console.log(outputArray);
If the properties of each of the objects isn't guaranteed either- you'd want to add checks for the properties themselves too. For example:
(item[0] && item[0].prop1 && item[0].prop2) ? 'stuff' : 'otherstuff'
You should check for the existance of particular element at index before using it. Here the relevant changes:
const outputArray = inputArray.map(function(item, i) {
var qt = "";
if (item.quantity[0]) {
qt += (item.quantity[0].qval + item.quantity[0].unit);
}
if (item.quantity[1]) {
qt += '|';
qt += (item.quantity[1].qval + item.quantity[1].unit);
}
return {
name: item.name,
amount: qt
};
});
Use a for loop to iterate through the length of item.quantity if there will be an uncertain number of items inside it:
const outputArray = inputArray.map(function(item, i) {
let amountStr = "";
for (i = 0; i < item.quantity.length; i++) {
amountStr += item.quantity[i].qval + item.quantity[i].unit;
// add delimiter when the current item is not the last one in the array
if (i < quantity.length - 1) amountStr += "|";
}
return {
name: item.name,
amount: amountStr
};
Related
Assuming I have 2 arrays:
const products = [
{
name: 'prod1',
category: 'Meat'
},
{
name: 'prod2',
category: 'Meat'
},
{
name: 'prod3',
category: 'Dairy'
}];
const rate = [
{
name: 'prod1',
rate: 23,
},
{
name: 'prod2',
rate: 36
},
{
name: 'prod3',
rate: 50,
}];
How would you get the category that has the highest sum rate? For example, prod1 and prod2 share the same category 'Meat" and hence the rate for meat is 36 + 23 = 59.
The way I thought about it is to create an adjusted Array of products where each entry will contain the rate from the second array and then I will create a result array and push an object of category and sumRate after iterating the adjustedArray.
So if the result Array has an object with category, I would adjust the sum and add the new rate, if not I'll create a new entry with category: rate.
Can we do this in a very optimal way?
As the OP probably knows, canonical grouping goes like this...
const prodsByCategory = products.reduce((acc, p) => {
let cat = p.category;
if (!acc[cat]) acc[cat] = [];
acc[cat].push(p);
return acc;
}, {});
Modify this a little to add the data which will be needed to optimize.
const prodsByCategory = products.reduce((acc, p) => {
let cat = p.category;
// instead of just an array, keep an array and a total
if (!acc[cat]) acc[cat] = { products: [], totalRate: 0 };
// instead of just pushing, push and increment total with a lookup
acc[cat].products.push(p);
acc[cat].totalRate += rateForProduct(p) || 0;
return acc;
}, {});
We need a lookup for rateForProduct, like this:
const rateForProduct = product => {
return rate.find(r => r.name === product.name)?.rate || 0;
}
That should produce an object keyed by category with values that have a prop called totalRate. Sort those entries so that the first one is maximized. Here's a demo...
const products = [{
name: 'prod1',
category: 'Meat'
},
{
name: 'prod2',
category: 'Meat'
},
{
name: 'prod3',
category: 'Dairy'
}
];
const rate = [{
name: 'prod1',
rate: 23,
},
{
name: 'prod2',
rate: 36
},
{
name: 'prod3',
rate: 50,
}
];
const rateForProduct = product => {
return rate.find(r => r.name === product.name)?.rate || 0;
}
const prodsByCategory = products.reduce((acc, p) => {
let cat = p.category;
if (!acc[cat]) acc[cat] = {
products: [],
totalRate: 0
};
acc[cat].products.push(p);
acc[cat].totalRate += rateForProduct(p);
return acc;
}, {});
const sortedEntries = Object.entries(prodsByCategory).sort((a, b) => b[1].totalRate - a[1].totalRate);
const bestEntry = {
category: sortedEntries[0][0],
rate: sortedEntries[0][1].totalRate
}
console.log(bestEntry);
I am trying to join the array of objects internal properties with the comma , like as below
const multiSpaceIdentityInformation = spaces?.reduce(
(acc, space) => {
acc.roomName = `${acc.roomName},${space.spaceIdentity.roomName}`;
acc.roomNumber = `${acc.roomNumber},${space.spaceIdentity.roomNumber}`;
acc.storyName = `${acc.storyName},${space.buildingStory?.name}`;
acc.spaceNumber = `${acc.spaceNumber},${space.spaceIdentity.number}`;
acc.spaceName = `${acc.spaceName},${space.spaceIdentity.name}`;
return acc;
},
{
roomName: 'N/A',
roomNumber: 'N/A',
storyName: 'N/A',
spaceNumber:'N/A',
spaceName: 'N/A'
}
);
But somehow, I cannot display the information even I have spaces holding the array of objects.
What I am trying to display is if there is no information, I would like to say 'N/A' as the default option.
I am accessing the above information here
const identityData = [
{ label: 'Room Number', value: multiSpaceIdentityInformation.roomNumber },
{ label: 'Room Name', value: multiSpaceIdentityInformation.roomName },
{ label: 'Level', value: multiSpaceIdentityInformation.storyName },
{ label: 'Space Number', value: multiSpaceIdentityInformation.spaceNumber },
{ label: 'Space Name', value: multiSpaceIdentityInformation.spaceName }
];
Could anyone please let me know where it goes wrong with the above code?
Many thanks in advance!
Sample input
{
"Spaces": [
{
"spaceGeometry":{
"roomName": ""
"roomNumber": "",
"number": "number1"
"name": "space1"
},
"buildingStory":{
"name": "story1"
}
},
{
"spaceGeometry":{
"roomName": ""
"roomNumber": "",
"number": "number2"
"name": "space2"
},
"buildingStory":{
"name": "story2"
}
},
]
}
and desired output be like
multiSpaceIdentityInformation.roomName = "N/A"
multiSpaceIdentityInformation.roomNumber = "N/A"
multiSpaceIdentityInformation.storyName = "story1, story2"
multiSpaceIdentityInformation.spaceNumber = "number1, number2"
multiSpaceIdentityInformation.spaceName = "space1, space2"
The second parameter of reduce() is the initial value, so "N/A" will be rendered as long as spaces is an array. Here's what I would do:
const appendData = (initialValue, newValue) => {
if(!newValue) return initialValue;
if(!initialValue || initialValue === '') return newValue;
return `${initialValue}, ${newValue}`;
}
const multiSpaceIdentityInformation = spaces?.reduce(
(acc, space) => ({
roomName: appendData(acc.roomName, space.spaceIdentity.roomName),
roomNumber: appendData(acc.roomNumber, space.spaceIdentity.roomNumber),
storyName: appendData(acc.storyName, space.buildingStory?.name),
spaceNumber: appendData(acc.spaceNumber, space.spaceIdentity.number),
spaceName: appendData(acc.spaceName, space.spaceIdentity.name)
}),
{
roomName: '',
roomNumber: '',
storyName: '',
spaceNumber:'',
spaceName: ''
}
);
Object.keys(multiSpaceIdentityInformation).forEach((key) => {
if(multiSpaceIdentityInformation[key] === '')
multiSpaceIdentityInformation[key] = 'N/A';
});
I have an array of objects:
items: [
{ name: "Cheese Puffs", price: 3 },
{ name: "Can of Soda", price: 1.75 }
];
I want to do something like items["cheesePuffs"] === true. But as it is in an array it won't work properly.
What you want is Array.find().
myArrOfObjects.find(el => el.cheesePuffs);
Assuming the property you're looking for is truthy, this returns the element, {cheesePuffs: "yes"} which is truthy. If it weren't found, it would be undefined.
You can use some and hasOwnProperty, If you need actual value instead of Boolean values you can use find instead of some
const myArrOfObjects = [{
cheesePuffs: "yes"
},
{
time: "212"
}];
let findByName = (name) => {
return myArrOfObjects.some(obj=> {
return obj.hasOwnProperty(name)
})
}
console.log(findByName("cheesePuffs"))
console.log(findByName("time"))
console.log(findByName("123"))
okay simple solutions
try this
const x = [{}];
if(x.find(el => el.cheesePuffs) == undefined)
console.log("empty objects in array ")
const myArrOfObjects = [{
cheesePuffs: "yes"
},
{
time: "212"
}];
if(myArrOfObjects.find(el => el.cheesePuffs) == undefined)
console.log("empty objects in array ")
else
console.log("objects available in array ")
As suggestion, you can also decide to not use an array, but to use a json object, where the index of each item is the unique name of your objects (in the example "cheesePuffs" identifies "Cheese Puffs")
let items = {
"cheesePuffs": {name: "Cheese Puffs",price: 3},
"canOfSoda": {name: "Can of Soda",price: 1.75},
};
console.log("exist: ", items.cheesePuffs!== undefined)
console.log(items.cheesePuffs)
// can also access to item in this way:
console.log(items["cheesePuffs"])
console.log("not exist", items.noCheesePuffs!== undefined)
console.log(items.noCheesePuffs)
Try the following code. It will return you the object where name matches to 'Cheese Puffs'.
let items = [{
name: "Cheese Puffs",
price: 3
},
{
name: "Can of Soda",
price: 1.75
}
];
let itemExist = items.find(x => x.name === 'Cheese Puffs');
if (itemExist) {
console.log(itemExist);
} else {
console.log("Item not Exist");
}
You can use find
let exist = myArrOfObjects.find(o => o.cheesePuffs === 'yes')
First of all you have an array of objects so you can't simply use
myArrOfObjects["cheesePuffs"]
because array required an index so it should be myArrOfObjects[0]["cheesePuffs"]
let items = [
{ name: "Cheese Puffs", price: 3 },
{ name: "Can of Soda", price: 1.75 }
];
let filter = items.find( el => el.price === 3 );
console.log(filter);
another approach
let items = [
{ name: "Cheese Puffs", price: 3 },
{ name: "Can of Soda", price: 1.75 }
];
let filter = items.filter( el => el.price === 3 );
console.log(filter);
how to assign the object in object and filter the value which pass and fail;
the input is:
[
{
name: 'John',
score: 90,
time: 'evening'
},
{
name: 'Doni',
score: 68,
time: 'morning'
},
{
name: 'Jiu',
score: 50,
time: 'evening'
},
{
name: 'Shin',
score: 92,
time: 'morning'
},
];
and i want the output like this :
{
"evening": {
"pass": [
{
"name": "John",
"score": 90
}
],
"fail": [
{
"name": "jiu",
"score": 50
}
]
},
"morning": {
"pass": [
{
"name": "Shin",
"score": 92
}
],
"fail": [
{
"name": "Doni",
"score": 68
}
]
}
}
do we need to use Object.assign for this ? and how many loop we use for this ??
i do love to know how to add another string in the object beside that ouput,
thanks
There's a lot of ways to do this. The simplest is probably to make a base object that represent your empty results. Then loop over the students and fill the arrays:
let students = [{name: 'John',score: 90,time: 'evening'},{name: 'Doni',score: 68,time: 'morning'},{name: 'Jiu',score: 50,time: 'evening'},{name: 'Shin',score: 92,time: 'morning'},];
// Empty case
let base = {
"evening": {"pass": [], "fail": []},
"morning": {"pass": [], "fail": []}
}
const PASSING = 70
students.forEach(({name, score, time}) => {
let key = score >= PASSING ? 'pass' : 'fail'
base[time][key].push({name, score})
})
console.log(base)
This makes is easy to have empty arrays, which is probably what you want if there are no students in a particular category.
EDIT based on comment:
To support arbitrary times, you can just create the times on the object as you find them. reduce() is good for this, but you could also use a regular loop. For example with an added afternoon time:
let students = [{name: 'Mark',score: 95,time: 'afternoon'}, {name: 'John',score: 90,time: 'evening'},{name: 'Doni',score: 68,time: 'morning'},{name: 'Jiu',score: 50,time: 'evening'},{name: 'Shin',score: 92,time: 'morning'},];
const PASSING = 70
let result = students.reduce((obj, {name, score, time}) => {
if (!obj[time]) obj[time] = {'pass': [], 'fail': [] }
let key = score >= PASSING ? 'pass' : 'fail'
obj[time][key].push({name, score})
return obj
}, {})
console.log(result)
You can do something like this:
const data = [{ name: 'John', score: 90, time: 'evening' }, { name: 'Doni', score: 68, time: 'morning' }, { name: 'Jiu', score: 50, time: 'evening' }, { name: 'Shin', score: 92, time: 'morning' }, ];
const grp = (d, p) => d.reduce((r,c) => (r[c[p]] = [...r[c[p]] || [], c], r), {})
const grpV = (d, rng) => d.reduce((r,{name, score}) => {
let key = score > rng ? 'pass' : 'fail'
r[key] = [...r[key] || [], {name, score}]
return r
}, {})
const r = Object.entries(grp(data, 'time')).map(([k,v]) => ({[k]: grpV(v, 75)}))
console.log(r)
The idea is the group 2 times one on the time and 2nd on the score.
grp: function to group by a property (in this case 'time') which returns an object with 2 properties: evening and morning each of which is an array containing the classes.
grpV: function to group by value (in this case 75) which returns an object with 2 properties: pass and fail each of which is an array containing the classes.
On the end once we have those tools we are saying ... give me the entries of the grouped by time object and for each of the groups ... group by score.
Here how something like this could look like if we ware using lodash:
const data = [{ name: 'John', score: 90, time: 'evening' }, { name: 'Doni', score: 68, time: 'morning' }, { name: 'Jiu', score: 50, time: 'evening' }, { name: 'Shin', score: 92, time: 'morning' }, ];
const partition = (x, p) => _(x)
.partition(y => y.score > p)
.map((x,i) => ({ [i==0 ? 'pass': 'fail']: _.omit(x[0], 'time')}))
.value()
const r = _(data)
.groupBy('time')
.mapValues(x => partition(x, 75))
.value()
console.log(r)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Adding it as an example since it does help with readability of what the ES6 example is doing to some extend.
I'm sure there are more elegant ways to do this. But this one is probably one of the simplest beginner-friendly ways you can go about this.
I loop through the input array, check the existence of the .time values as keys on the output object and create the pass and fail keys. Then evaluate the .score against the passingScore and push the necessary data to it.
Should be pretty easy to understand once you see and try the code below:
const data = [
{name: 'John',score: 90, time: 'evening'},
{name: 'Doni',score: 68, time: 'morning'},
{name: 'Jiu',score: 50, time: 'evening'},
{name: 'Shin',score: 92, time: 'morning'},
{name: 'Fubar',score: 75, time: 'noon'},
];
function formatData(data){
const passingScore = 75;
const output = {};
data.forEach(function(item){
if(!output[item.time]) output[item.time] = {pass: [], fail: []};
const stud = { name: item.name, score: item.score };
if(item.score >= passingScore) output[item.time]['pass'].push(stud)
else output[item.time]['fail'].push(stud)
});
return output;
}
console.log(formatData(data));
I have an array
[
{ price: 10 },
{ price: 10 },
{ price: 10 },
{ price: 10 },
{ price: 20 },
{ price: 20 },
]
and I want it transformed into
[
{ numElements: 4, price: 10 },
{ numElements: 2, price: 20 },
]
I have tried using arr.reduce((prev, curr) => ..., []) to accomplish this, but I can't figure out how to do it.
A traditional method might use a for/loop to wrangle the data, but these days JavaScript has a number of functional methods that can help. This code uses reduce and map. To get your data in the format you want is a two stage process.
First, use reduce to create a hash table using the price as a key (because you know the each price is going to be unique:
const obj = arr.reduce((p, c) => {
// If price exists as a key its value by 1
// otherwise set it to 1.
p[c.price] = ++p[c.price] || 1;
return p;
}, {});
OUTPUT
{
"10": 4,
"20": 2
}
As it stands you've got a perfectly good object that you can access by the key/price and I would probably just stop there:
obj['10'] // 4
But if you want to get that data into the format in your question, map over the object keys to return an array of new objects.
const out = Object.keys(obj).map(key => {
return { price: +key, numElements: obj[key] };
});
DEMO
var hash = {}, result = [];
arr.forEach(function(el){
if(hash[el.price]){
hash[el.price].numElements++;
}else{
result.push(hash[el.price]={price:el.price,numElements:1});
}
});
Run
May use a hash table for price lookup. Or with reduce and find:
arr.reduce((res,{price})=>
(( res.find(el=>el.price===price) || res[res.push({price,numElements:0})-1] )
.numElements++,res)
);
Run
You can use try this:
let arr = [
{ price: 10 },
{ price: 10 },
{ price: 10 },
{ price: 10 },
{ price: 20 },
{ price: 20 },
]
let result = []
let counter = {}
arr.forEach( el => {
if (!counter[el.price]) counter[el.price] = 1
else counter[el.price]++
console.log(counter[el.price])
})
for (let id in counter) {
result.push({numElements: counter[id], price: id})
}
Assuming that the data comes sorted on price property, with a single .reduce() you may do as follows;
var data = [{ price: 10 }, { price: 10 }, { price: 10 }, { price: 10 }, { price: 20 }, { price: 20 }],
result = data.reduce((r,d,i) => i ? r[r.length-1].price === d.price ? (r[r.length-1].numElemenets++, r)
: (r.push(Object.assign({}, d, {numElemenets: 1})),r)
: [Object.assign({}, d, {numElemenets: 1})], {});
console.log(result);
You could look up the price in the result array and if not found insert a new object.
var data = [{ price: 10 }, { price: 10 }, { price: 10 }, { price: 10 }, { price: 20 }, { price: 20 }],
grouped = data.reduce((r, { price }) => {
var t = r.find(p => price === p.price);
t || r.push(t = { numElements: 0, price });
t.numElements++;
return r;
}, []);
console.log(grouped);