Transform Nested Object Data Structure into an Array of Objects- JavaScript - javascript

I am having trouble with this problem taking an object and reformatting it to a new data structure. I need to take the beginning object and do the following: sort by group first, then label and exclude "active: false" records.
var beginning = {
Sister: {
1: { id: 1, name: 'Jesse Steven', active: false },
2: { id: 2, name: 'Zena Wong', active: true },
3: { id: 3, name: 'Katie Johnson', active: true },
},
Brother: {
10: { id: 10, name: 'Jeff Jacobs', active: true },
11: { id: 11, name: 'Mark Matha', active: false },
12: { id: 12, name: 'Kyle Ford', active: true },
},
Friend: {
20: { id: 20, name: 'Jim Dobbs', active: true },
}
};
After, it should looks like this:
var final = [
{ label: 'Jeff Jacobs', value: 10, group: 'Brother' },
{ label: 'Kyle Ford', value: 12, group: 'Brother' },
{ label: 'Jim Dobbs', value: 20, group: 'Friend' },
{ label: 'Katie Johnson', value: 3, group: 'Sister' },
{ label: 'Zena Wong', value: 2, group: 'Sister' }
];

Like this?
It's still missing a sort, but that can be easily remedied.
let beginning = {
Sister: {
1: { id: 1, name: 'Jesse Steven', active: false },
2: { id: 2, name: 'Zena Wong', active: true },
3: { id: 3, name: 'Katie Johnson', active: true },
},
Brother: {
10: { id: 10, name: 'Jeff Jacobs', active: true },
11: { id: 11, name: 'Mark Matha', active: false },
12: { id: 12, name: 'Kyle Ford', active: true },
},
Friend: {
20: { id: 20, name: 'Jim Dobbs', active: true },
}
};
let relations = Object.keys(beginning)
let final = relations.map(function(relation){
let num_keys = Object.keys(beginning[relation])
return num_keys.map(function(num_key){
beginning[relation][num_key]["group"] = relation
return beginning[relation][num_key]
})
})
.reduce(function(a, b){//flattens the returned array of arrays
return a.concat(b);
})
.filter(function(a){//filters out only active
return a["active"]
})
.map(function(a){//clean up some data
return {
label: a["name"],
value: a["id"],
group: a["group"]
}
})
console.log(final)

EDIT: Added sorting as the initial requirements asked for.
You can do this a number of ways, including for / in loops or some fancy stuff with ES2015, but a relatively simple functional example solution would be the following:
var activePeople = Object.keys(beginning).map(person => {
return Object.keys(beginning[person]).map(num => {
return (!!beginning[person][num].active) ? {
label: beginning[person][num].name,
value: beginning[person][num].id,
group: person
} : null
}).filter(i => !!i)
})
// flatten nested arrays
var final = [].concat.apply([], activePeople).sort((p1, p2) => {
if (p1.group < p2.group) {
return -1
} else if (p1.group > p2.group) {
return 1
}
if (p1.label < p2.label) {
return -1
}
return 1
})

I can propose faster one code:
"use strict";
let beginning = {
Sister: {
1: { id: 1, name: 'Jesse Steven', active: false },
2: { id: 2, name: 'Zena Wong', active: true },
3: { id: 3, name: 'Katie Johnson', active: true },
},
Brother: {
10: { id: 10, name: 'Jeff Jacobs', active: true },
11: { id: 11, name: 'Mark Matha', active: false },
12: { id: 12, name: 'Kyle Ford', active: true },
},
Friend: {
20: { id: 20, name: 'Jim Dobbs', active: true },
}
};
let groups = Object.keys(beginning).sort();
let final = [];
for (let i = 0, max = groups.length; i < max; i++) {
let keys = Object.keys(beginning[groups[i]]);
for (let j = 0, max2 = keys.length; j < max2; j++) {
let item = beginning[groups[i]][keys[j]];
if (item['active'] ) {
final.push({
label: item['name'],
value: keys[j],
group: groups[i]
});
}
}
}
console.log(final);

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

Grouping array of objects by multiple properties

I have an array of notifications that I want to group by certain conditions (like facebook's notifications)
var data = [
{ id: 1, type: 'shop.follower', by: { id: 2, name: 'User B', }, in: null, read_at: '2021-01-03 10:15:43', created_at: '2020-08-02 05:21:20' },
{ id: 2, type: 'product.liked', by: { id: 2, name: 'User B' }, in: { id: 1, ... }, read_at: '2021-01-03 10:15:43', created_at: '2020-08-02 05:24:45' },
{ id: 3, type: 'product.commented', by: { id: 3, name: 'User C' }, in: { id: 1, ... }, read_at: '2021-02-20 20:01:39', created_at: '2021-02-19 16:21:43' },
{ id: 4, type: 'product.liked', by: { id: 4, name: 'User D' }, in: { id: 1, ... }, read_at: '2021-03-29 15:14:21', created_at: '2021-03-28 08:11:50' },
{ id: 5, type: 'product.liked', by: { id: 3, name: 'User C' }, in: { id: 1, ... }, read_at: null, created_at: '2021-03-28 08:12:24' },
{ id: 6, type: 'shop.follower', by: { id: 5, name: 'User E' }, in: null, read_at: null, created_at: '2021-05-23 10:02:21' },
{ id: 7, type: 'shop.follower', by: { id: 3, name: 'User C' }, in: null, read_at: null, created_at: '2021-07-18 10:31:12' },
{ id: 8, type: 'comment.replied', by: { id: 4, name: 'User D' }, in: { id: 6, ... }, read_at: null, created_at: '2021-07-24 08:34:25' },
]
Let's say I want to group by date in descending order
So I have this code:
function sortByDate(array, desc = true) {
if (desc === false) {
// Ascending order
return array.sort((a, b) => {
if (new Date(a.created_at) > new Date(b.created_at)) {
return 1
} else {
return -1
}
return 0
})
}
// Descending order
return array.sort((a, b) => {
if (new Date(a.created_at) < new Date(b.created_at)) {
return 1
} else {
return -1
}
return 0
})
}
So now we have array like this:
[
{ id: 8, type: 'comment.replied', by: { id: 4, name: 'User D' }, in: { id: 6, ... }, read_at: null, created_at: '2021-07-24 08:34:25' },
{ id: 7, type: 'shop.follower', by: { id: 3, name: 'User C' }, in: null, read_at: null, created_at: '2021-07-18 10:31:12' },
{ id: 6, type: 'shop.follower', by: { id: 5, name: 'User E' }, in: null, read_at: null, created_at: '2021-05-23 10:02:21' },
{ id: 5, type: 'product.liked', by: { id: 3, name: 'User C' }, in: { id: 1, ... }, read_at: null, created_at: '2021-03-28 08:12:24' },
{ id: 4, type: 'product.liked', by: { id: 4, name: 'User D' }, in: { id: 1, ... }, read_at: '2021-03-29 15:14:21', created_at: '2021-03-28 08:11:50' },
{ id: 3, type: 'product.commented', by: { id: 3, name: 'User C' }, in: { id: 1, ... }, read_at: '2021-02-20 20:01:39', created_at: '2021-02-19 16:21:43' },
{ id: 2, type: 'product.liked', by: { id: 2, name: 'User B' }, in: { id: 1, ... }, read_at: '2021-01-03 10:15:43', created_at: '2020-08-02 05:24:45' },
{ id: 1, type: 'shop.follower', by: { id: 2, name: 'User B', }, in: null, read_at: '2021-01-03 10:15:43', created_at: '2020-08-02 05:21:20' },
]
Now that our array is sorted, I created a function:
// https://www.tutorialspoint.com/most-efficient-method-to-groupby-on-an-array-of-objects-in-javascript
function groupByProperty(array, property) {
return array.reduce((acc, object) => {
const key = object[property]
if (! acc[key]) {
acc[key] = []
}
acc[key].push(object)
return acc
}, {})
}
Then, I run this code
Object.values(groupByProperty(data, 'type'))
Which return:
[
[
{ id: 8, type: 'comment.replied', by: { id: 4, name: 'User D' }, in: { id: 6, ... }, read_at: null, created_at: '2021-07-24 08:34:25' }
],
[
{ id: 7, type: 'shop.follower', by: { id: 3, name: 'User C' }, in: null, read_at: null, created_at: '2021-07-18 10:31:12' },
{ id: 6, type: 'shop.follower', by: { id: 5, name: 'User E' }, in: null, read_at: null, created_at: '2021-05-23 10:02:21' },
{ id: 1, type: 'shop.follower', by: { id: 2, name: 'User B', }, in: null, read_at: '2021-01-03 10:15:43', created_at: '2020-08-02 05:21:20' }
],
[
{ id: 5, type: 'product.liked', by: { id: 3, name: 'User C' }, in: { id: 1, ... }, read_at: null, created_at: '2021-03-28 08:12:24' },
{ id: 4, type: 'product.liked', by: { id: 4, name: 'User D' }, in: { id: 1, ... }, read_at: '2021-03-29 15:14:21', created_at: '2021-03-28 08:11:50' },
{ id: 2, type: 'product.liked', by: { id: 2, name: 'User B' }, in: { id: 1, ... }, read_at: '2021-01-03 10:15:43', created_at: '2020-08-02 05:24:45' }
],
[
{ id: 3, type: 'product.commented', by: { id: 3, name: 'User C' }, in: { id: 1, ... }, read_at: '2021-02-20 20:01:39', created_at: '2021-02-19 16:21:43' }
],
]
I want to group these notifications by these categories:
Same type (I already covered this in my function groupByProperty())
Same in: { id: ... } Except for type: shop.follower
If #1 and #2 were true, check for similar objects with created_at: ... date interval between 10 minutes
If we have a case like #3 (multiple), if one among it has read_at = null, then it will be an unread notification, get the latest (newest) date
In id: 4 and id: 5, interval between timestamp are less than 10 minutes, so I want it to group as one
example EXPECTED OUTPUT:
[
[
{ by: {id: 4, name: "User D"}, created_at: "2021-07-24 08:34:25", id: 8, in: {id: 6}, read_at: null, type: "comment.replied" }
],
[
{ by: {id: 3, name: "User C"}, created_at: "2021-07-18 10:31:12", id: 7, in: null, read_at: null, type: "shop.follower" }
],
[
{ by: {id: 5, name: "User E"}, created_at: "2021-05-23 10:02:21", id: 6, in: null, read_at: null, type: "shop.follower" }
],
[
{ by: {id: 3, name: "User C"}, created_at: "2021-03-28 08:12:24", id: 5, in: {id: 1}, read_at: null, type: "product.liked" },
{ by: {id: 4, name: "User D"}, created_at: "2021-03-28 08:11:50", id: 4, in: {id: 1}, read_at: "2021-03-29 15:14:21", type: "product.liked" }
],
[
{ by: {id: 3, name: "User C"}, created_at: "2021-02-19 16:21:43", id: 3, in: {id: 1}, read_at: "2021-02-20 20:01:39", type: "product.commented" }
],
[
{ by: {id: 2, name: "User B"}, created_at: "2020-08-02 05:24:45", id: 2, in: {id: 1}, read_at: "2021-01-03 10:15:43", type: "product.liked" }
],
[
{ by: {id: 2, name: "User B"}, created_at: "2020-08-02 05:21:20", id: 1, in: null, read_at: "2021-01-03 10:15:43", type: "shop.follower" }
],
]
example IN BROWSER:
|------------------------------------------------------------------------------|
| - (UNREAD) User D replied to your comment ....., 2021-07-24 08:34:25 |
| - (UNREAD) User C start follow your shops ....., 2021-07-18 10:31:12 |
| - (UNREAD) User E start follow your shops ....., 2021-05-23 10:02:21 |
| - (UNREAD) User C and D liked your product ....., 2021-03-28 08:12:24 | <= (Please pay attention)
| - (READ) User C commented on your product ....., 2021-02-19 16:21:43 |
| - (READ) User B liked your product ....., 2020-08-02 05:24:45 |
| - (READ) User B start follow your shops ....., 2020-08-02 05:21:20 |
This is the code I tried to find interval between 10 minutes
function inRangeBetween(val, min, max) {
if (val >= min && val <= max) {
return true
}
return false
}
var startingPoint = { min: 0, max: 0, type: null },
newData = []
for (var i = 0; i < data.length; i++) {
if (startingPoint.min < 1
&& startingPoint.max < 1
&& startingPoint.type === null) {
console.log('Starting point')
var start = new Date(data[i].created_at)
startingPoint.min = start.getTime()
startingPoint.max = start.getTime() + (10 * 60000)
startingPoint.type = data[i].type
newData[data[i].type] = []
} else {
// startingPoint has values
if (inRangeBetween(new Date(data[i].created_at).getTime(), startingPoint.min, startingPoint.max
&& data[i].type === startingPoint.type) {
console.log(`Pushing new object to key ${data[i].type}`)
newData[data[i].type].push(data[i])
} else {
// Set new values for startingPoint, and start again comparing
console.log('Starting point values changes')
startingPoint.min = new Date(data[i]).getTime()
startingPoint.min = new Date(data[i]).getTime() + (10 * 60000)
startingPoint.type = data[i].type
newData[data[i].type] = []
newData[data[i].type].push(data[i])
}
}
}
// Not working
How to achieve this? (Stuck in this problem for 5 days)
Thanks in advance
Try like this:
var data = [
{ id: 1, type: 'shop.follower', by: { id: 2, name: 'User B', }, in: null, read_at: '2021-01-03 10:15:43', created_at: '2020-08-02 05:21:20' },
{ id: 2, type: 'product.liked', by: { id: 2, name: 'User B' }, in: { id: 1 }, read_at: '2021-01-03 10:15:43', created_at: '2020-08-02 05:24:45' },
{ id: 3, type: 'product.commented', by: { id: 3, name: 'User C' }, in: { id: 1 }, read_at: '2021-02-20 20:01:39', created_at: '2021-02-19 16:21:43' },
{ id: 4, type: 'product.liked', by: { id: 4, name: 'User D' }, in: { id: 1 }, read_at: '2021-01-03 10:15:43', created_at: '2021-03-28 08:11:50' },
{ id: 5, type: 'product.liked', by: { id: 3, name: 'User C' }, in: { id: 1 }, read_at: null, created_at: '2021-03-28 08:12:24' },
{ id: 6, type: 'shop.follower', by: { id: 5, name: 'User E' }, in: null, read_at: null, created_at: '2021-05-23 10:02:21' },
{ id: 7, type: 'shop.follower', by: { id: 3, name: 'User C' }, in: null, read_at: null, created_at: '2021-07-18 10:31:12' },
{ id: 8, type: 'comment.replied', by: { id: 4, name: 'User D' }, in: { id: 6 }, read_at: null, created_at: '2021-07-24 08:34:25' }
]
function sortByDate(array, desc = true) {
if (desc === false) {
// Ascending order
return array.sort((a, b) => {
if (new Date(a.created_at) > new Date(b.created_at)) {
return 1
} else {
return -1
}
return 0
})
}
// Descending order
return array.sort((a, b) => {
if (new Date(a.created_at) < new Date(b.created_at)) {
return 1
} else {
return -1
}
return 0
})
}
function groupByProperties(array, properties) {
return Object.values(array.reduce((acc, object) => {
const key = properties.reduce((acc, property) => {
return acc + (object[property] ? JSON.stringify(object[property]) : '')
}, '')
if (! acc[key]) {
acc[key] = []
}
acc[key].push(object)
return acc
}, {}))
}
function groupByInterval(data, interval) {
var group;
for(var i = 0; i < data.length; i++) {
if(1 < data[i].length) {
var max_date = new Date(data[i][0].created_at);
for(var j = data[i].length - 1; 0 < j; j--) {
var next_date = new Date(data[i][j].created_at)
if(interval < max_date - next_date) {
if(!group) {
group = i + 1;
data.splice(group, 0, [])
}
data[group].splice(0, 0, data[i][j])
data[i].splice(j, 1)
};
};
if(group) {
return groupByInterval(data, interval)
};
}
data[i].sort((a, b) => {
if(!a.read_at) {
return -1
}
if(!b.read_at) {
return 1
}
return 0
})
}
data.sort((a, b) => new Date(b[0].created_at) - new Date(a[0].created_at))
return data
}
sortByDate(data)
//1. Same type
//2. Same in: { id: ... } (Except for type: shop.follower)
data = groupByProperties(data, ['type', 'in'])
//3. If #1 and #2 true, check for similar objects with created_at: ... date gap between 10 minutes
//4. If we have a case like #3 (multiple), if one among it has read_at = null, then it unread notification, then get the latest (newest) date
data = groupByInterval(data, 1000 * 60 * 10) //10min
console.log(data)
groupByProperties() is based on groupByProperty(), but accepts multiple properties for grouping (categories 1 and 2). It checks whether the value of the property is falsy (such as null), excluding it from grouping criteria if so.
groupByInterval() added to separate groups according to a specified interval in milliseconds (categories 3 and 4). It then sorts the groups according to read_at being falsy, so that objects with read_at == null appear first in each group. It then sorts across groups to achieve the order in the expected result.
Mr.sbgib is absolutely correct, but i just modified sortByDate function to a little short as follows to reduce duplicate codes,
function sortByDate(array, desc = true) {
return array.sort((a, b) => {
var compare = new Date(a.created_at) < new Date(b.created_at);
return (desc == true) ? ((compare == true) ? 1 : -1) : ((compare == false) ? 1 : -1);
})
}

filter through multiple arrays and add objects from them to a single array

Example of an object in the accounts array:
const accounts = [
{
id: "5f446f2ecfaf0310387c9603",
picture: "https://api.adorable.io/avatars/75/esther.tucker#zillacon.me",
age: 25,
name: {
first: "Esther",
last: "Tucker",
},
company: "ZILLACON",
email: "esther.tucker#zillacon.me",
registered: "Thursday, May 28, 2015 2:51 PM",
},
Example of an object in the books array:
const books = [
{
id: "5f447132d487bd81da01e25e",
title: "sit eiusmod occaecat eu magna",
genre: "Science",
authorId: 8,
borrows: [
{
id: "5f446f2e2cfa3e1d234679b9",
returned: false,
},
{
id: "5f446f2ed3609b719568a415",
returned: true,
},
{
id: "5f446f2e1c71888e2233621e",
returned: true,
},
{
id: "5f446f2e6059326d9feb9a68",
returned: true,
},
{
id: "5f446f2ede05a0b1e3394d8b",
returned: true,
},
{
id: "5f446f2e4081699cdc6a2735",
returned: true,
},
{
id: "5f446f2e3900dfec59489477",
returned: true,
},
{
id: "5f446f2e6059326d9feb9a68",
returned: true,
},
{
id: "5f446f2e409f8883af2955dd",
returned: true,
},
{
id: "5f446f2e3900dfec59489477",
returned: true,
},
{
id: "5f446f2eae901a82e0259947",
returned: true,
},
{
id: "5f446f2ef2ab5f5a9f60c4f2",
returned: true,
},
{
id: "5f446f2ea6b68cf6f85f6e28",
returned: true,
},
{
id: "5f446f2eed18105706d6ca19",
returned: true,
},
{
id: "5f446f2eae901a82e0259947",
returned: true,
},
{
id: "5f446f2e91c2af00cb74e82b",
returned: true,
},
{
id: "5f446f2e5aa2bb5545a0f8a6",
returned: true,
},
{
id: "5f446f2ea508b6a99c3e42c6",
returned: true,
},
{
id: "5f446f2e50cc2da9cd80efdb",
returned: true,
},
{
id: "5f446f2e0b3e2ff72fc503e7",
returned: true,
},
{
id: "5f446f2e91c2af00cb74e82b",
returned: true,
},
{
id: "5f446f2ef795e593cd3cd19d",
returned: true,
},
{
id: "5f446f2e2f35653fa80bf490",
returned: true,
},
{
id: "5f446f2e7b9cd304fed3a8bc",
returned: true,
},
{
id: "5f446f2ed9aac23c0340aab2",
returned: true,
},
],
},
Example of objects in the authors array:
const authors = [
{
id: 0,
name: {
first: "Lucia",
last: "Moreno",
},
},
{
id: 1,
name: {
first: "Trisha",
last: "Mathis",
},
},
{
id: 2,
name: {
first: "Arnold",
last: "Marks",
},
},
I need to write the function function getBooksPossessedByAccount(account, books, authors) {} that does the following: It returns an array of books and authors that represents all books currently checked out by the given account. Look carefully at the object below, as it's not just the book object; the author object is embedded inside of it.
Output example:
getBooksPossessedByAccount(account, books, authors);
[
{
id: "5f447132320b4bc16f950076",
title: "est voluptate nisi",
genre: "Classics",
authorId: 12,
author: {
id: 12,
name: {
first: "Chrystal",
last: "Lester",
},
},
borrows: [
{
id: "5f446f2e6059326d9feb9a68",
returned: false,
},
...
],
},
]
Here's what I have so far:
function getBooksPossessedByAccount(account, books, authors) {
const accId = account.id;
const result = [];
for (let idxBks = 0; idxBks < books.length; idxBks++) {
if (
books[idxBks].borrows.id === accId &&
books[idxBks].borrows.returned === false
) {
result.push(books[idxBks]);
}
for (let idxAuth = 0; idxAuth < authors.length; idxAuth++) {
let authorIdx = authors[idxAuth];
if (authorIdx.id === result.authorId) {
return [result, { author: authorIdx }];
}
}
}
return result;
}
You need to search all the borrows, not just borrows[0]. You can use the some() method to check all of them.
Since the author information needs to be added as a property to the book object, you shouldn't be pushing it onto the booksOut array.
function getBooksPossessedByAccount(account, books, authors) {
const accId = account.id;
const booksOut = books.filter(
(book) => book.borrows.some(borrow => !borrow.returned && borrow.id === accId)
);
booksOut.forEach(book => book.author = authors.find(author => book.authorID == author.id))
return booksOut;
}
Using some should do the trick..
function getBooksPossessedByAccount(account, books, authors) {
let borrowedBooks=books.filter(book=>
book.some(borrow=>borrow.id===account.id)
)
return borrowedBooks //array of book objects
//return borrowedBooks.map(book=>book.id) //to show array of book ids
}

Javascript function- transform from one data structure to another

I'm trying to build a JS function to convert the data structure in the form of
'start' to the form of 'expected'.
Using JS map() method, how would I do this for the following associative array-
const start = {
Clients: {
171: { id: 171, name: 'John Smith', active: false },
172: { id: 172, name: 'Jacob Jacobson', active: true },
1441: { id: 1441, name: 'Eric Ericsson', active: true },
},
Caregivers: {
1: { id: 1, name: 'John Johnson', active: true },
37: { id: 37, name: 'James Jameson', active: false },
15: { id: 15, name: 'Aaron Aaronson', active: true },
},
Doctors: {
1147: { id: 1147, name: 'Doc Docson', active: true },
},
Hospitals: {
115: { id: 115, active: false, name: "St. Mary's" },
},
Applicants: {
17345: { id: 17345, name: 'Bob Bobson', active: true },
17346: { id: 17346, name: 'Jeff Jeffson', active: false },
17347: { id: 17347, name: 'Frank Frankson', active: true },
17348: { id: 17348, name: 'Bill Billson', active: true },
},
};
needs to be converted to-
const expected = [
{ label: 'Bill Billson', value: 17348, group: 'Applicants' },
{ label: 'Bob Bobson', value: 17345, group: 'Applicants' },
{ label: 'Frank Frankson', value: 17347, group: 'Applicants' },
{ label: 'Aaron Aaronson', value: 15, group: 'Caregivers' },
{ label: 'John Johnson', value: 1, group: 'Caregivers' },
{ label: 'Eric Ericsson', value: 1441, group: 'Clients' },
{ label: 'Jacob Jacobson', value: 172, group: 'Clients' },
{ label: 'Doc Docson', value: 1147, group: 'Doctors' },
];
.map() can't be used directly on Objects; instead you'll need to use Object.keys
const start = {
Clients: {
171: { id: 171, name: 'John Smith', active: false },
172: { id: 172, name: 'Jacob Jacobson', active: true },
1441: { id: 1441, name: 'Eric Ericsson', active: true }
},
Caregivers: {
1: { id: 1, name: 'John Johnson', active: true },
37: { id: 37, name: 'James Jameson', active: false },
15: { id: 15, name: 'Aaron Aaronson', active: true }
},
Doctors: {
1147: { id: 1147, name: 'Doc Docson', active: true }
},
Hospitals: {
115: { id: 115, active: false, name: "St. Mary's" }
},
Applicants: {
17345: { id: 17345, name: 'Bob Bobson', active: true },
17346: { id: 17346, name: 'Jeff Jeffson', active: false },
17347: { id: 17347, name: 'Frank Frankson', active: true },
17348: { id: 17348, name: 'Bill Billson', active: true }
}
};
// Get an array of properties in 'start'
// then use Array.reduce() to loop over each item
const expected = Object.keys(start).reduce( (res, gKey) => {
// gKey = 'group' name
// gVal = 'group' value
let gVal = start[gKey];
// loop over each item in the 'group'
Object.keys(gVal).forEach(iKey => {
// iKey = 'group.item' name
// iVal = 'group.item' value
let iVal = gVal[iKey];
// if the value's .active property is truthy
if (iVal.active) {
// format the result as desired and add it to the result array
res.push({
label: iVal.name,
value: iKey,
group: gKey
});
}
});
// return the result array
return res;
// start the .reduce() with an empty array
}, []);
console.log(expected);
To loop over an object, you can either use a for ... in loop, or use Object.keys to get an array of keys. For ... in will include inherited properties, so you may need to manually filter them out. Object.keys only returns own properties, so there's no need to do the filtering (but it also isn't appropriate if you need inherited properties)
Example with for ... in:
for (var prop in start) {
if (start.hasOwnProperty(prop)) {
// logs out 'Clients', then 'Caregivers', then 'Doctors', then 'Hospitals', then 'Applicants'
console.log(prop);
}
}
Example with Object.keys:
//produces array ['Clients', 'Caregivers', 'Doctors', 'Hospitals', 'Applicants']
var keys = Object.keys(start);
So if you wanted to use .map, you can start with this, and fill it in to do whatever you desire:
Object.keys(start)
.map(key => {
//do something with start[key]
//perhaps you could get Object.keys(start[key]) and loop over that as well.
});
My solution without 'forEach':
function transform(data) {
Object.entries(data).map(item => Object.values(item[1])
.map(i => i.group = item[0]))
.reduce(( acc, cur ) => acc.concat(cur), [])
.filter(item => item.active === true)
.sort((a, b) => a.group - b.group)
.map(item => {
let expected = {};
expected.label = item.name;
expected.value = item.id;
expected.group = item.group;
});
return expected;
}

Chai-related error message: "AssertionError: expected undefined to deeply equal"

I wrote a function that when given a list of objects and an
id, returns the same list, but with the corresponding object marked active
(all other objects should not be active).
const list = [
{ id: 1, active: false },
{ id: 2, active: false },
{ id: 3, active: true },
{ id: 4, active: false }
];
function markActive(list, value) {
list.forEach((id) => {
if (id.active = (id.id === value)) {
return true;
} else {
return false;
}
});
}
markActive(list, 2);
console.log(list)
Returns:
[ { id: 1, active: false },
{ id: 2, active: false },
{ id: 3, active: false },
{ id: 4, active: true } ]
It's working like a charm, except when I run "npm run [filename]" I get an error message:
Running Tests for [filename].
------------
[ { id: 1, active: false },
{ id: 2, active: false },
{ id: 3, active: false },
{ id: 4, active: true } ]
markActive
1) Case 1 (Given Sample)
2) Case 2 (String IDs)
0 passing (16ms)
2 failing
1) markActive Case 1 (Given Sample):
AssertionError: expected undefined to deeply equal [ { id: 1,
active: false },
{ id: 2, active: true },
{ id: 3, active: false },
{ id: 4, active: false } ]
at Function.assert.deepEqual
(node_modules/chai/lib/chai/interface/assert.js:216:32)
at Context.it (tests/test_02.js:23:12)
2) markActive Case 2 (String IDs):
AssertionError: expected undefined to deeply equal [ { id: '1',
active: false },
{ id: '2', active: true },
{ id: '3', active: false },
{ id: '4', active: false } ]
at Function.assert.deepEqual
(node_modules/chai/lib/chai/interface/assert.js:216:32)
at Context.it (tests/test_02.js:40:12)
Any idea what I'm doing wrong? Here's the code that sets up the tests:
const chai = require("chai");
const sinon = require("sinon");
const assert = chai.assert;
const markActive = require("../answers/02.js");
describe("markActive", () => {
it("Case 1 (Given Sample)", () => {
var list = [
{ id: 1, active: false },
{ id: 2, active: false },
{ id: 3, active: true },
{ id: 4, active: false }
];
var newList = markActive(list, 2);
var targetList = [
{ id: 1, active: false },
{ id: 2, active: true },
{ id: 3, active: false },
{ id: 4, active: false }
];
assert.deepEqual(newList, targetList);
});
it("Case 2 (String IDs)", () => {
var list = [
{ id: "1", active: false },
{ id: "2", active: false },
{ id: "3", active: true },
{ id: "4", active: false }
];
var newList = markActive(list, "2");
var targetList = [
{ id: "1", active: false },
{ id: "2", active: true },
{ id: "3", active: false },
{ id: "4", active: false }
];
assert.deepEqual(newList, targetList);
});
});
Your function isn't returning anything, so any variables you try to set to the result will be set as undefined.
To fix this, simply add a return statement to the end of your function.
function markActive(list, value) {
list.forEach((id) => {
if (id.active = (id.id === value)) {
return true;
} else {
return false;
}
});
return list; // return the updated list
}
NOTE: It's worth mentioning that because the array is referenced, you're modifying the values in-place. This is why the array you defined outside the function still had updated results even though you weren't logging the returned value. This can have unintended side effects if you were to run the markActive() function several times on the same list. If you want a new list to be returned, look into ways of copying and deep copying arrays in Javascript.

Categories

Resources