Destructuring object consist of deep nested object of arrays - javascript

I'm trying to destructure setup shown below. I need to get text: Zelena The Wicked Witch is an enemy to Emma Swan in Once Upon a Time.
OK, it's easy to get properties from objects info and protagonist, but I can't get data from an array of objects (enemies) for particular object, for instance for row number 3.
I tried many different expressions, with no luck.
Any help would be highly appreciated.
function nestedArrayAndObject() {
// refactor this to a single line of destructuring...
const info = {
title: 'Once Upon a Time',
protagonist: {
name: 'Emma Swan',
enemies: [
{name: 'Regina Mills', title: 'Evil Queen'},
{name: 'Cora Mills', title: 'Queen of Hearts'},
{name: 'Peter Pan', title: `The boy who wouldn't grow up`},
{name: 'Zelena', title: 'The Wicked Witch'},
],
},
}
const {
protagonist: {
enemies[3]: {name: enemyName}
},
protagonist: {
enemies: {title: enemyTitle}
},
protagonist: {name: protagonistName},
title: title
} = info;
return `${enemyName} (${enemyTitle}) is an enemy to ${protagonistName} in "${title}"`
}
nestedArrayAndObject();

It's not that hard. Take a look at this.
const info = {title: 'Once Upon a Time', protagonist: {name: 'Emma Swan', enemies: [ {name: 'Regina Mills', title: 'Evil Queen'}, {name: 'Cora Mills', title: 'Queen of Hearts'}, {name: 'Peter Pan', title: 'The boy who wouldn\'t grow up'}, {name: 'Zelena', title: 'The Wicked Witch'} ]}};
const {protagonist: {enemies: [,, {name, title}]}} = info;
console.log(name, title)

You can achieve as:
const {
title,
protagonist: {
name: protagonistName,
enemies: [, , , { name: enemyName, title: enemyTitle }],
},
} = info;
function nestedArrayAndObject() {
// refactor this to a single line of destructuring...
const info = {
title: 'Once Upon a Time',
protagonist: {
name: 'Emma Swan',
enemies: [
{ name: 'Regina Mills', title: 'Evil Queen' },
{ name: 'Cora Mills', title: 'Queen of Hearts' },
{ name: 'Peter Pan', title: `The boy who wouldn't grow up` },
{ name: 'Zelena', title: 'The Wicked Witch' },
],
},
};
const {
title,
protagonist: {
name: protagonistName,
enemies: [, , , { name: enemyName, title: enemyTitle }],
},
} = info;
return `${enemyName} (${enemyTitle}) is an enemy to ${protagonistName} in "${title}"`;
}
nestedArrayAndObject();

Related

How would you change values/add to nested object data inside array of objects using Javascript?

const beers = [
{
id: '100',
name: 'stoneys'
},
{
id: '200',
name: 'budweiser'
},
{
id: '300',
name: 'miller'
},
{
id: '400',
name: 'corona'
}
];
const people = [
{
name: 'steve',
teams: [
{
name: 'pirates',
beers: ['100']
},
{
name: 'penguins',
beers: ['300']
}
]
},
{
name: 'jim',
teams: [
{
name: 'indians',
beers: ['200']
},
{
name: 'blue jackets',
beers: ['100', '400']
}
]
}
];
let newPeople = people.map(fan => {
fan.teams.map(team => {
team.beers.map(beer => beers.filter(brand => brand.id === beer)[0])
});
});
Above is a sample I put together to best demonstrate my question. I am having trouble understanding why nested mapping (.map()) of object arrays is not allowing me to alter the nested data. When I console log results, I am either getting an "[undefined, undefined]' or the unchanged "people" array.
I would like to return the same array as "people" except replace the nested "beers" array (people.teams.beers[]) with corresponding objects from the "beers" array. Example of a successful result below:
{
name: 'steve',
teams: [
{
name: 'pirates',
beers: [
{
id: '100',
name: 'stoneys'
}
]
},
{
name: 'penguins',
beers: [
{
id: '300',
name: 'miller'
}
]
}
]
}
Array.map expects a function which takes single array element as parameter and returns a mapped value. In your case you're not returning any value from mapping functions therefore you're getting undefined twice
const beers = [
{
id: '100',
name: 'stoneys'
},
{
id: '200',
name: 'budweiser'
},
{
id: '300',
name: 'miller'
},
{
id: '400',
name: 'corona'
}
];
const people = [
{
name: 'steve',
teams: [
{
name: 'pirates',
beers: ['100']
},
{
name: 'penguins',
beers: ['300']
}
]
},
{
name: 'jim',
teams: [
{
name: 'indians',
beers: ['200']
},
{
name: 'blue jackets',
beers: ['100', '400']
}
]
}
];
let newPeople = people.map(fan => {
let teams = fan.teams.map(team => {
let beer = team.beers.map(beer => beers.filter(brand => brand.id === beer)[0]);
return { name: team.name, beers: beer }
});
return { name: fan.name, teams: teams }
});
console.log(newPeople);

forEach loop returns undefined value

I've dummy data (books) which I want see from graphiql gui. But when I use a forEach loop to iterate through the books, looking for a specific id, it's returning undefined values, but if I use a normal for loop it works fine.
This is my code:
let books = [
{ name: 'Name of the Wind', genre: 'Horror', id: '1', authorID: '3' },
{ name: 'The Final Empire', genre: 'Fantasy', id: '2', authorID: '1' },
{ name: 'The Long Earth', genre: 'Sci-Fi', id: '3', authorID: '2' },
];
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
book: {
type: BookType,
args: { id: { type: GraphQLString } },
//this forEach is not working
resolve(parent, args){
books.forEach( function(book) {
if(book.id == args.id) {
console.log(book);
return book;
}
});
}
}
}
});
When I print out the book data, it's showing that particular book in the console but not in the GUI response:
request:
{
book(id: "2") {
name
genre
}
}
response:
{
"data": {
"book": null
}
}
A return <value> in a forEach callback is meaningless. The returned value goes nowhere, and the loop is not interrupted either.
Instead use .find:
return books.find(function(book) {
return book.id == args.id;
});
When performance is important, and you have lots of books, then it is better to first preprocess the books and create a Set:
let books = [
{ name: 'Name of the Wind', genre: 'Horror', id: '1', authorID: '3' },
{ name: 'The Final Empire', genre: 'Fantasy', id: '2', authorID: '1' },
{ name: 'The Long Earth', genre: 'Sci-Fi', id: '3', authorID: '2' },
];
let bookIds = new Set(books.map(({id}) => id));
... and then no loop is needed to know whether a book ID is valid or not:
return bookIds.has(args.id);

How to find a property in a nested object

I'm trying to figure out how to extract result based on an object property value that is inside another object e.g
{
title: 'The Book',
author: {
name: 'Merlin Bridge',
category: 'Fiction'
}
}
Here are my tests:
// this works
const test1 = _.find(existingBooks, {
title: uploadedBooks[i].title,
});
// this doesn't work and returns nothing
const test2 = _.find(existingBooks, {
'author.name': uploadedBooks[i].author.name,
});
I want to get the result wherein the author.name is the identifier. Thanks a lot.
As you are using Lodash/Underscore find, I took the liberty to also use the get helper. Please find below an answer which should help you.
const books = [{
title: 'The Book',
author: {
name: 'Merlin Bridge',
category: 'Fiction'
}
},
{
title: 'Another Book',
author: {
name: 'Merlin Window',
category: 'Science'
}
}
];
const uploadedBooks = [{
title: 'Another Book',
author: {
name: 'Merlin Window',
category: 'Science'
}
}];
const i = 0;
const foundBook = _.find(books, (book, index) => {
return _.get(book, 'author.name') === _.get(uploadedBooks, [i, 'author', 'name'])
});
console.log(foundBook);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.10/lodash.min.js"></script>
You can Use below code to iterate your json object.
var myObj = {
title: 'The Book',
author: {
name: 'Merlin Bridge',
category: 'Fiction'
}
}
Object.keys(myObj).forEach(key => {
console.log(JSON.stringify(myObj[key]));
});
It's possible to add dynamically named properties to JavaScript objects.
Assuming you are looping an array and not an object:
var a = _.map(existingBooksArray, function(existingBook) {
return {[existingBook.author.name]: existingBook.title}
});
Result of a:
[
{
Merlin Bridge: "The Book"
}
]
use .key method
Object.keys(myObj).forEach(key => {
console.log(myObj[key]);});
var myObj = {
title: 'The Book',
author: {
name: 'Merlin Bridge',
category: 'Fiction'
}
}
Object.keys(myObj).forEach(key => {
console.log(JSON.stringify(myObj[key]));
});
var myObj = {
title: 'The Book',
author: {
name: 'Merlin Bridge',
category: 'Fiction'
}
}
Object.keys(myObj).forEach(key => {
console.log(JSON.stringify(myObj[key]));
});

How to iterate through an array of object properties, in an array of objects

I have an array of objects, that looks like this:
data = [
{
title: 'John Doe',
departments: [
{ name: 'Marketing', slug: 'marketing'},
{ name: 'Sales', slug: 'sales'},
{ name: 'Administration', slug: 'administration'},
]
},
{
title: 'John Doe Junior',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Sales', slug: 'sales'},
]
},
{
title: 'Rick Stone',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Marketing', slug: 'marketin'},
]
},
]
How can I iterate over each object's departments array and create new arrays where I would have employees sorted by departments, so that the end result would like this:
operations = [
{
title: 'John Doe Junior',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Sales', slug: 'sales'},
]
},
{
title: 'Rick Stone',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Marketing', slug: 'marketin'},
]
},
]
marketing = [
{
title: 'John Doe',
departments: [
{ name: 'Marketing', slug: 'marketing'},
{ name: 'Sales', slug: 'sales'},
{ name: 'Administration', slug: 'administration'},
]
},
{
title: 'Rick Stone',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Marketing', slug: 'marketin'},
]
},
]
What would be the way to create dynamically this kind of arrays?
Update
I have tried to come up with a solution using the suggestion from the answer, where I would dynamically create an array with department objects that would have an array of employees:
const isInDepartment = departmentToCheck => employer => employer.departments.find(department => department.slug == departmentToCheck);
var departments = [];
function check(departments, name) {
return departments.some(object => name === object.department);
}
employees.forEach((employee) => {
employee.departments.forEach((department) => {
let found = check(departments, department.slug);
if (!found) {
departments.push({ department: department.slug });
}
});
});
departments.forEach((department) => {
// push an array of employees to each department
//employees.filter(isInDepartment(department));
});
But, I don't know how can I push the array of employees to the object in the array that I am looping at the end?
This is the fiddle.
How about this? I use Array.protoype.filter operation, and I use a higher-order function (in this case a function that returns a function) to create the predicate (function that returns a boolean) that will check whether an employee is in a specific department. I added some (hopefully) clarifying comments in the code.
Edit: with the new code and context you provided this JSFiddle demo shows how it would work together.
const employees = [
{
title: 'John Doe',
departments: [
{ name: 'Marketing', slug: 'marketing'},
{ name: 'Sales', slug: 'sales'},
{ name: 'Administration', slug: 'administration'}
]
},
{
title: 'John Doe Junior',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Sales', slug: 'sales'}
]
},
{
title: 'Rick Stone',
departments: [
{ name: 'Operations', slug: 'operations'},
{ name: 'Marketing', slug: 'marketin'}
]
}
];
// given a department, this returns a function that checks
// whether an employee is in the specified department
// NOTE: the "find" returns the found object (truthy)
// or undefined (falsy) if no match was found.
const isInDepartment =
departmentToCheck =>
employee => employee.departments.find(dep => dep.name == departmentToCheck);
const employeesInMarketing = employees.filter(isInDepartment('Marketing'));
const employeesInOperations = employees.filter(isInDepartment('Operations'));
console.log('Employees in marketing', employeesInMarketing);
console.log('Employees in operations', employeesInOperations);

Backbone Relational - Deep nested models/collections

Having some fun with Backbone-relational.js v0.10.0
I have a JSON object which contains nested models and collections, to keep this simple I have made the example below. A company can have many employees, employees can have one address and many tasks.
{
name: 'Nicks Company',
employees: [{
name: 'Bob',
address: {
line1: '1 City Lane',
city: 'London'
},
tasks: [{
description: 'Make this work'
}]
}, {
name: 'Bill',
address: {
line1: '1 The Street',
city: 'Birmingham'
},
tasks: [{
description: 'Make a cake'
}, {
description: 'Fix all the things'
}]
}]
}
In the below JSFiddle (console) you can see that the address is a plain object, also the commented out code for tasks has been disabled as tasks is returned as an array.
JSFiddle: https://jsfiddle.net/nwa29uLv/2/
Neither the address model or the collection of tasks are created as Backbone instances, is this possible or am I pushing the limits of this plugin?
Here is how I resolved the issue. You were missing the
reverseRelation.
BTW did you look at this post? It has a similar issue.
Follow the fiddle here
var Address = Backbone.RelationalModel.extend({});
var Task = Backbone.RelationalModel.extend({});
var TaskCollection = Backbone.Collection.extend({
model: Task
});
var Employee = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasOne,
key: 'address',
relatedModel: Address,
reverseRelation: {
type: Backbone.HasOne,
key: 'employee'
}
}, {
type: Backbone.HasMany,
key: 'tasks',
collectionType: TaskCollection,
relatedModel: Task,
reverseRelation: {
type: Backbone.HasOne,
key: 'operatesOn'
}
}]
});
var EmployeeCollection = Backbone.Collection.extend({
model: Employee
});
var Company = Backbone.RelationalModel.extend({
relations: [{
type: Backbone.HasMany,
key: 'employees',
relatedModel: Employee,
reverseRelation: {
key: 'worksFor'
}
}]
});
var company = new Company({
name: 'Nicks Company',
employees: [{
name: 'Bob',
address: {
line1: '1 City Lane',
city: 'London'
},
tasks: [{
description: 'Make this work'
}]
}, {
name: 'Bill',
address: {
line1: '1 The Street',
city: 'Birmingham'
},
tasks: [{
description: 'Make a cake'
}, {
description: 'Fix all the things'
}]
}]
});
console.log('Company:', company.get('name'));
company.get('employees').each(function(employee) {
console.log('Employee:', employee.get('name'));
console.log('Employee:', employee.get('name'), 'Address Model:', employee.get('address').get('city'));
employee.get('tasks').each(function(task) {
console.log('Employee:', employee.get('name'), 'Task: ', task.get('description'));
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone-relational/0.10.0/backbone-relational.js"></script>

Categories