Trying to map objects inside an object - javascript

I'm a beginner at webdev and I'm having a little problem with reactjs. I have an array of data about universities, like:
const data = [
{
name: "UFSM",
total: 17,
filled: 12,
approved: 10,
reproved: 1,
dropouts: 1,
requests: 170,
approved_requests: 120,
disciplines: [{name: "engineering", value: 10}, {name:"biology", value: 20}, ...]
},
{...
disciplines: [{name: "engineering", value: 7}, ....]
},...]
I'm using the map function to map all their names into an array, like:
let data_name = props.data.map((data) => data.name);
Is it possible to use map to group into an array the value of disciplines with the same name from different universities? Having an output like:
data_engineering = ["10", "7", ...]
So that I could have all engineering related disciplines from different universities on the same array.

const data = [{
name: "UFSM",
total: 17,
filled: 12,
approved: 10,
reproved: 1,
dropouts: 1,
requests: 170,
approved_requests: 120,
disciplines: [{
name: "engineering",
value: 10
}, {
name: "biology",
value: 20
}]
}, {
name: "ABCD",
total: 17,
filled: 12,
approved: 10,
reproved: 1,
dropouts: 1,
requests: 170,
approved_requests: 120,
disciplines: [{
name: "engineering",
value: 4
}, {
name: "biology",
value: 5
}]
}, {
name: "EFGH",
total: 17,
filled: 12,
approved: 10,
reproved: 1,
dropouts: 1,
requests: 170,
approved_requests: 120,
disciplines: [{
name: "engineering",
value: 5
}, {
name: "biology",
value: 9
}]
}]
let result = {};
data.map((data) => data.disciplines).forEach((item, index) => {
item.forEach((item, index) => {
if (!result[item.name]) {
result[item.name] = []
}
result[item.name].push(item.value);
});
});
console.log(JSON.stringify(result));

If you want an array for a single discipline like engineering, you can do the following map:
data_engineering =
props.data.map((uni) => uni.disciplines.engineering);
This array will have an undefined value for a university that doesn't have an engineering value defined. But that is probably what you want, because it means the indices line up with the indices of data_name.
If you want to build such an array for all disciplines, you can do the following:
const dataDiscipline = {};
// 1. Collect all disciplines from all universities
// by making them a key of dataDiscipline.
props.data.forEach((uni) =>
uni.disciplines.forEach((discipline) =>
dataDiscipline[discipline] = null
)
);
// 2. Now build the discipline array for each discipline.
Object.keys(dataDiscipline).forEach((discipline) =>
dataDiscipline[discipline] =
props.data.map((uni) => uni.disciplines[discipline])
);
// You can now use e.g. dataDiscipline.engineering

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

find an object in Array of Array

if i want for an example loop Through this Array to find specific item in Items Array how to approach that? i made this logic but it doesn't work
DATA.map((D)=>{
return D.items.find((item)=>{
return item.name ==='Blue Beanie'
})
})
this is the Array plus how to create new ONE array includes the both of items arrays to be like that: items: [{
id: 1,
name: 'Brown Brim',
price: 25
},
{
id: 2,
name: 'Blue Beanie',
price: 18
},
{
id: 3,
name: 'Adidas NMD',
price: 220
},
{
id: 4,
name: 'Adidas Yeezy',
price: 280
}
]
const DATA= [
{
id: 1,
title: 'Hats',
routeName: 'hats',
items: [
{
id: 1,
name: 'Brown Brim',
price: 25
},
{
id: 2,
name: 'Blue Beanie',
price: 18
}
]
},
{
id: 2,
title: 'Sneakers',
routeName: 'sneakers',
items: [
{
id: 3,
name: 'Adidas NMD',
price: 220
},
{
id: 4,
name: 'Adidas Yeezy',
price: 280
}
]
}
];
Transform DATA into list of items and find from that list your expected item
const res = DATA.flatMap((D) => D.items).find(
(item) => item.name === "Brown Brim"
)
const DATA = [
{
id: 1,
title: "Hats",
routeName: "hats",
items: [
{
id: 1,
name: "Brown Brim",
price: 25,
},
{
id: 2,
name: "Blue Beanie",
price: 18,
},
],
},
{
id: 2,
title: "Sneakers",
routeName: "sneakers",
items: [
{
id: 3,
name: "Adidas NMD",
price: 220,
},
{
id: 4,
name: "Adidas Yeezy",
price: 280,
},
],
},
]
const res = DATA.flatMap((D) => D.items).find(
(item) => item.name === "Brown Brim"
)
console.log(res)
Reference
Array.prototype.flatMap()
Maybe this is helpful?
const DATA= [
{id: 1,title:'Hats',routeName:'hats',
items:[{id: 1,name:"Brown Brim",price:25},
{id: 2,name: 'Blue Beanie',price: 18}]},
{id: 2,title: 'Sneakers',routeName: 'sneakers',
items: [{id: 3,name: 'Adidas NMD',price: 220},
{id: 4,name: 'Adidas Yeezy',price: 280}]}
];
console.log(DATA.map(D=>D.items.find(item=>item.name==='Brown Brim'))
.filter(e=>e))
The .map returns either an element matching your criterion or undefined, The chained .filter then removes all "falsy" elements, i. e. all the undefined ones.
As for the first question "loop Through this Array to find a specific item in Items Array"
given it is not sorted in any way, this can be done by iterating over the DATA array and search inside the items
If want to have access to the item from the outside of the 'forEach' scope you have to declare the variable outside
Regarding the second question, use the reduce function while iterating the array
NOTE: You can obviously combine both tasks as you already iterate through the array, so no need to do it twice. But to avoid confusion, I separated the logic.
Also, if you do choose to combine the tasks, using the reduce is not relevant, but very much like the answer to the first question, you can declare a buffer such as an array, and just copy items to it on the go (I'll leave out questions on performance for that matter)
const DATA = [
{
id: 1, title: 'Hats', routeName: 'hats',
items: [
{id: 1,name: 'Brown Brim',price: 25},
{id: 2,name: 'Blue Beanie',price: 18}
]
},
{
id: 2, title: 'Sneakers', routeName: 'sneakers',
items: [
{id: 3,name: 'Adidas NMD',price: 220},
{id: 4,name: 'Adidas Yeezy',price: 280}
]
}
];
//Question 1
//creating the object that will hold the item
//outside of the 'forEach' scope so we can use it later
let res = {};
const searchRes = DATA.forEach(entry => {
entry.items.forEach(item => {
if (item.name === 'Brown Brim')
//duplicating the item to a variable declared outside of this scope
res = { ...item
};
});
});
console.log(`Q1 - The item we found:`);
console.log(res);
// Question 2
// Merging all object inside the 'items' array using Reduce
const merged = DATA.reduce((acc, curr) =>
acc.concat(curr.items), []);
console.log(`Q2 - The merged array:`);
console.log(merged)

Finding proprieties of accumulator in JS

I am running this accumulator, it works great but I find it challenging to add some proprities in the same object :
let expenseCategories = expenseObj.reduce((acc, obj) => {
let category = obj['category'];
// let amount = obj['amount'];
if (category in acc) {
acc[category] += 1;
} else {
acc[category] = 1;
}
return acc;
}, {});
{Transportation: 2, Food: 1, Clothes: 1, Bills: 2, Fun: 1, …}
My initial object also contains a transaction amount
{amount: 10, category: "Transportation", date: 20190510, expense: true, ...
{amount: 20, category: "Drinks", date: 20190510, expense: true, ...
{amount: 30, category: "Bills", date: 20190510, expense: true, ...
{amount: 40, category: "Bills", date: 20190510, expense: true, ...
My goal here is to calculate sum of each categories like bills in the example above would be 70.
I am looking to add this info to display some chart, the expected array looks something like that :
0: {name: "Transportation", value: 2, total: 123}
1: {name: "Food", value: 1, total: 456}
2: {name: "Clothes", value: 1, total: 789}
This is my attempt with the correct data I already have:
let outExpense = Object.entries(expenseCategories).map(([name, value]) => ({ name, value }));
This should be dynamic because I don't know categories prior to running code. These are user inputs. Please advise. Thanks
You are counting the occurrences of each category. You need to change your implementation a little bit. Instead of creating a number as value, you can create the object you need in the output array as value. Then, simply use Object.values() on the accumulator object to get the array of grouped values
const expenseObj = [{amount: 10, category: "Transportation", date: 20190510, expense: true},
{amount: 20, category: "Drinks", date: 20190510, expense: true},
{amount: 30, category: "Bills", date: 20190510, expense: true},
{amount: 40, category: "Bills", date: 20190510, expense: true}]
let groupCategories = expenseObj.reduce((acc, obj) => {
let { category, amount } = obj;
if (category in acc) {
acc[category].value++;
acc[category].total += amount;
} else {
acc[category] = { name: category, value: 1, total: amount };
}
return acc;
}, {});
console.log(Object.values(groupCategories))
This is how the accumulator would look like after the changes. If the current object's category already exists in the accumulator, increment the value and total. Else, add the category as key and a new object as it's value
{
"Transportation": {
"name": "Transportation",
"value": 1,
"total": 10
},
"Drinks": {
"name": "Drinks",
"value": 1,
"total": 20
},
"Bills": {
"name": "Bills",
"value": 2,
"total": 70
}
}

Javascript map over array of obj with another array to get different key value

So I am not sure why I having such a difficult time with this, but I have an array of ids that I am trying to use to map over an array of objects to find the corresponding id but return the value from a different key.
i.e.:
arr=[13, 1, 16]
arrObj= [{
id: 1,
name: "cat"
}, {
id: 10,
name: "tiger",
}, {
id: 3,
name: "dog",
}, {
id: 16,
name: "bear",
}, {
id: 8,
name: "fish",
}, {
id: 13,
name: "goat",
}]
and I want it to return:
["goat", "cat", "bear"]
I have a nested map function that does this but returns undefined for the objects that do not have a corresponding ID. I could filter out the undefineds from the returned array, but it seems that there is a cleaner/more efficient way to do this.
What is the cleanest way to achieve this?
You could use Array#map and search with Array#find for the corresponding object. Then take name as return value.
var arr = [13, 1, 16],
arrObj = [{ id: 1, name: "cat" }, { id: 10, name: "tiger" }, { id: 3, name: "dog" }, { id: 16, name: "bear" }, { id: 8, name: "fish" }, { id: 13, name: "goat" }],
result = arr.map(id => arrObj.find(o => o.id === id).name);
console.log(result);
For a lots of data, you could take a Map and build it by mapping key value pairs and then map the result of the map.
var arr = [13, 1, 16],
arrObj = [{ id: 1, name: "cat" }, { id: 10, name: "tiger" }, { id: 3, name: "dog" }, { id: 16, name: "bear" }, { id: 8, name: "fish" }, { id: 13, name: "goat" }],
result = arr.map(
Map.prototype.get,
new Map(arrObj.map(({ id, name }) => [id, name]))
);
console.log(result);
Try this:
var arr=[13, 1, 16];
var arrObj= [{
id: 1,
name: "cat"
}, {
id: 10,
name: "tiger",
}, {
id: 3,
name: "dog",
}, {
id: 16,
name: "bear",
}, {
id: 8,
name: "fish",
}, {
id: 13,
name: "goat",
}];
var result = arr.map(id => arrObj.find(x => x.id == id)).map(x => x.name)
console.log(result);
// ["goat", "cat", "bear"]
.map() (from MDN web docs):
method creates a new array with the results of calling a provided
function on every element in the calling array.
.find() (from MDN web docs):
method returns the value of the first element in the array that
satisfies the provided testing function. Otherwise undefined is
returned.
There have been a lot of good answers already, however i feel the need to push for newer syntax as it is greater to work with in the long run.
const getNamesBasedOnIds = (arr, listOfIds) => {
return arr.reduce(((sum, element) =>
[...sum, ...(listOfIds.includes(element.id) ? [element.name] : [])]),
[]);
}
const animals = getNamesBasedOnIds(arrObj, [13,1,16]);
Using Array.filter, then Array.map requires you to run through the Array max twice.
With Array.reduce you can add elements to the sum if they exists in listOfIds, then after running through the arrObj once you have the result
[...sum, ...(listOfIds.includes(element.id) ? [element.name] : [])] is pretty slow, but its more for showing the use of spear operators
The above is equivalent to
sum.concat(listOfIds.includes(element.id) ? element.name : [])]
The fastest way, is to use Array.push
if(listOfIds.includes(element.id)){
sum.push(element.name);
}
return sum;
The correct way is to not nest any loops:
reduce your array of objects to a map of 'id-values' idValueMap
map through your array of 'ids' idArr using the idValueMap to get
the corresponding values for each id in constant time.
Thought Process
Understand that the biggest problem is the data itself. Don't let insufficient data types or structures force you you have a bad solution/code. Transform the data such to what you need so that you can create a proper solution.
Basically imagine if the objArr was just a Map that you could use to look up the values for an id.... then the solution is straight forward. So let's make that happen and then the rest falls into place. Data Data Data is what I always say :)
Rule of thumb NEVER run a loop inside of a loop if you can help it. FYI: filter, find, map, indexOf .... are all loops internally, do not nest them unless you absolutely must.
This solution is by far the most performant. This way you are not running O(n^2) (very BAD), you are instead running O(n) (very GOOD):
const idArr = [ 13, 1, 16 ];
const objArr= [
{
id: 1,
name: "cat"
},
{
id: 10,
name: "tiger",
},
{
id: 3,
name: "dog",
},
{
id: 16,
name: "bear",
},
{
id: 8,
name: "fish",
},
{
id: 13,
name: "goat",
}
];
const idValueMap = objArr.reduce((acc, { id, name }) => ({ ...acc, [id]: name }), {});
let output = idArr.map((id) => idValueMap[id]);
console.log(output);
You can first use .filter to filter and then use .map to output the desired properties
here
var arr = [13, 1, 16],
arrObj = [{
id: 1,
name: "cat"
}, {
id: 10,
name: "tiger",
}, {
id: 3,
name: "dog",
}, {
id: 16,
name: "bear",
}, {
id: 8,
name: "fish",
}, {
id: 13,
name: "goat",
}];
var res = arrObj.filter(o => arr.indexOf(o.id) >= 0);
console.log(res.map(o => o['name']))

Ordering array with hierarchy by score, possibly using lodash. (nodejs)

I am a bit stuck on this one: I have a array of objects ordered according to a hierarchy (parent_id), something as follow:
let myArray = [
{ id: 1, parent_id: null, score: 20, type: 1 },
{ id: 12, parent_id: 1, score: 25, type: 2 },
{ id: 23, parent_id: 12, score: 55, type: 3 },
{ id: 35, parent_id: 12, score: 25, type: 3 },
{ id: 10, parent_id: null, score: 75, type: 1 },
{ id: 25, parent_id: 10, score: 15, type: 2 },
{ id: 100, parent_id: 25, score: 88, type: 3 }
]
Now I would like to maintain the hierarchy order, but also order the elements by score to obtain something like this:
let expected = [
{ id: 10, parent_id: null, score: 75, type: 1 },
{ id: 25, parent_id: 10, score: 15, type: 2 },
{ id: 100, parent_id: 25, score: 88, type: 3 },
{ id: 1, parent_id: null, score: 20, type: 1 },
{ id: 12, parent_id: 1, score: 25, type: 2 },
{ id: 23, parent_id: 12, score: 55, type: 3 },
{ id: 35, parent_id: 12, score: 25, type: 3 },
]
I am writing pretty inefficient code with nested foreach that almost work, but nor quite yet. I was wondering if there was a neater solution. (pretty sure there is, but too smart for me).
Also in my code I am relaying on the type attribute, but ideally I would not use it for sorting.
Note: this data is just an example, the real array is bigger and the number of children for each parent vary.
Since my explanation is not great we can think of hierarchy in this way
type:1 -> Country
type:2 -> State
type:3 -> City
So I need to order by score desc like this
- Country
- State
- City
- City
- State
- City
- Country and so on...
Thank you to anyone who is willing to land me an hand,
A single sort does not work, because of the parent children relation which is not respected while sorting the data.
This approach works in three parts:
Sort data by score, because the following tree is build in insertation order.
Build a tree with the given relationship.
Traverse the tree and get sorted flat data back.
var data = [{ id: 1, parent_id: null, score: 20, type: 1 }, { id: 12, parent_id: 1, score: 25, type: 2 }, { id: 23, parent_id: 12, score: 55, type: 3 }, { id: 35, parent_id: 12, score: 25, type: 3 }, { id: 10, parent_id: null, score: 75, type: 1 }, { id: 25, parent_id: 10, score: 15, type: 2 }, { id: 100, parent_id: 25, score: 88, type: 3 }]
.sort(function (a, b) { return b.score - a.score; }),
tree = function (data, root) {
var r = [], o = {};
data.forEach(function (a) {
o[a.id] = { data: a, children: o[a.id] && o[a.id].children };
if (a.parent_id === root) {
r.push(o[a.id]);
} else {
o[a.parent_id] = o[a.parent_id] || {};
o[a.parent_id].children = o[a.parent_id].children || [];
o[a.parent_id].children.push(o[a.id]);
}
});
return r;
}(data, null), // null is the root value of parent_id
sorted = tree.reduce(function traverse(r, a) {
return r.concat(a.data, (a.children || []).reduce(traverse, []));
}, [])
console.log(sorted);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I changed your data set to make the example easier to understand. And I used several functions to sort the data:
let arr = [
{country: 'USA', state:'Washington', city:'Washington DC'},
{country: 'USA', state:'Washington', city:'Boston'},
{country: 'USA', state:'California', city:'San Diego'},
{country: 'Brazil', state:'North', city:'C'},
{country: 'Brazil', state:'North', city:'B'},
{country: 'Brazil', state:'South', city:'A'},
{country: 'Turkey', state:'East', city:'f'},
{country: 'Turkey', state:'East', city:'e'},
{country: 'Turkey', state:'West', city:'d'},
];
let expected = [
{country: 'Brazil', state:'North', city:'B'},
{country: 'Brazil', state:'North', city:'C'},
{country: 'Brazil', state:'South', city:'A'},
{country: 'Turkey', state:'East', city:'e'},
{country: 'Turkey', state:'East', city:'f'},
{country: 'Turkey', state:'West', city:'d'},
{country: 'USA', state:'California', city:'San Diego'},
{country: 'USA', state:'Washington', city:'Boston'},
{country: 'USA', state:'Washington', city:'Washington DC'},
];
const sortByCountry = arr => {
return arr.sort((city1,city2) => city1.country > city2.country);
};
const sortByState = arr =>
arr.sort(
(city1,city2) => {
// Don't compare when countries differ.
if (city1.country !== city2.country) return 0;
return city1.state > city2.state;
}
);
const sortByCity = arr =>
arr.sort(
(city1,city2) => {
// Don't compare when countries or states differ.
if (city1.country !== city2.country) return 0;
if (city1.state !== city2.state) return 0;
return city1.city > city2.city;
}
);
let result = sortByCity(sortByState(sortByCountry(arr)));
console.log('------expected-----');
console.log(expected);
console.log('------result-----');
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources