How to get a subset of a Javascript object with nested properties? - javascript

I'm developing with Angular and I have the following Typescript array of objects:
docs = [
{
id: '1',
type: {
id: 1
desc: 'Category 1',
}
title: 'Foo",
date: '2018-06-21',
attachments: [
{ id: 51, filename: 'foo.pdf', title: 'Foo' },
{ id: 20, filename: 'bar.doc', title: 'Bar' }
]
},
{
id: '2',
type: {
id: 2
desc: 'Category 2',
}
title: 'Bar",
date: '2018-06-21',
attachments: [
{ id: 15, filename: 'foobar.xls', title: 'Foobar' },
{ id: 201, filename: 'example.doc', title: 'Example' }
]
}
]
I need to get only a subset of the properties, something like this:
docs = [
{
id: '1',
type: {
id: 1
desc: 'Category 1',
}
attachments: [
{ id: 51 },
{ id: 20 }
]
},
{
id: '2',
type: {
id: 2
desc: 'Category 2',
}
attachments: [
{ id: 15 },
{ id: 201 }
]
}
]
How can I achieve this?
Have I to create a parser or does exist any smart way (such as Lodash) to extract a lite version of the array?

var docs = [{"id":"1","type":{"id":1,"desc":"Category 1"},"title":"Foo","date":"2018-06-21","attachments":[{"id":51,"filename":"foo.pdf","title":"Foo"},{"id":20,"filename":"bar.doc","title":"Bar"}]},{"id":"2","type":{"id":2,"desc":"Category 2"},"title":"Bar","date":"2018-06-21","attachments":[{"id":15,"filename":"foobar.xls","title":"Foobar"},{"id":201,"filename":"example.doc","title":"Example"}]}];
const result = docs.map(({id,type,attachments})=>{
let doc={id,type};
doc.attachments=attachments.map(({id})=>({id}));
return doc;
});
console.log(result);
have a look at this. this works perfectly!

You can use array.map and object destructuring to extract only the wanted properties.
Also use JSON.parse and JSON.stringify to make a copy and avoid side effetcs.
docs2 = JSON.parse(JSON.stringify(docs)).map(
({id, type, attachements}) =>
({ id,
type,
attachements: attachements.map(({id}) => ({id})
})
)

You can use Array.map with object spreading, something like this:
const mapSubset = ({ id, type, attachments }) => {
return { id, type, attachments: attachments.map( {id} => id ) };
};
const subset = docs.map( mapSubset );

I was looking for a non-specific way to accomplish this or any other similar cases, so far I've thought of the following:
Have an IMapping<T> type, that defines the way to map each property.
Have an IMappingFunction<T> interface, that determines how to map a specific thing:
The following code demonstrates it:
type IMapping<T> = {
[P in keyof T]: IMapping<T[P]> | IMappingFunction<T[P]>;
}
interface IMappingFunction<T>{
(t: T): T | Partial<T>
}
class Person{
name: string;
lastName: string;
}
const obj: IMapping<Person> = {
name: s => s.toUpperCase(),
lastName: s => s
}
function map<T>(obj: T, mapping: IMapping<T>) {
return Object.keys(obj)
.map(prop => {
const propMapping = mapping[prop];
return {
key: prop,
value: typeof propMapping === 'function' ?
propMapping(obj[prop]) :
map(obj, propMapping)
};
})
.reduce((acc, prop) => ({...acc, [prop.key]: prop.value}), { });
}
console.log(map({ name: 'Name', lastName: 'LastName'}, obj));
For a runnable snippet check here

do you need to leave the original array intact? If not you can iterate through the list of objects using a for loop and use the 'delete' operator to delete the properties you no longer want.
For example:
var Employee = {
firstname: "Mohammed",
lastname: "Haddad"
}
delete Employee.firstname;
console.log(Employee);
// expected output: { lastname: "Haddad" }

Related

How to remove objects from root of array if a duplicated is found in a Children object

The current code works with no problem. I like to know if there is a cleaner or better way to remove objects from the root of my array if a value is found somewhere in a nested object. I want to remove the object
const baseArray = [
{
title: 'Test folder',
UUID: '54F5E250-1C5F-49CC-A963-5B7FD8884E48',
Children: [
{
title: 'nothing',
UUID: 123,
},
]
},
{
title: 'nothing',
UUID: 123,
},
]
const currArray = baseArray
const removeRootUUID = (baseArray, currArray) => {
const delInRoot = uuidRef => {
baseArray.forEach((obj, i) => {
if (obj.UUID === uuidRef) {
baseArray.splice(i, 1);
}
});
};
for (const obj of currArray) {
if (obj.Children) {
obj.Children.forEach(node => {
delInRoot(node.UUID);
});
removeRootUUID(baseArray, obj.Children);
}
}
};
output I am looking for is below. (because UUID is founded in root and in another nested object)
{
title: 'Test folder',
UUID: '54F5E250-1C5F-49CC-A963-5B7FD8884E48',
Children: [
{
title: 'nothing',
UUID: 123,
},
],
}

Filtering array of objects if specific key contains search term

I am trying to filter through an object with multiple key/value pairs by a specific key. It appears that the code I've written is searching the entire object regardless of the key...
If key name contains the search term, return the search term.
Array of Objects:
export const someArrayOfObjects = [
{ id: '1', name: 'Something' },
{ id: '2', name: 'Another' },
{ id: '3', name: 'Lets do one more' },
]
Search:
const searchResults = someArrayOfObjects.filter((o) =>
Object.keys(o).some((k) => o[k].toString().toLowerCase().includes(searchTerm.toLowerCase()))
);
So if I search "Something", I only want it to loop through name to search for that term...
You don't need the Object.keys loop.
const someArrayOfObjects = [
{ id: '1', name: 'Something' },
{ id: '2', name: 'Another' },
{ id: '3', name: 'Lets do one more' },
];
let key = 'name';
let searchTerm = 'th';
const res = someArrayOfObjects.filter(o =>
o[key].toLowerCase().includes(searchTerm.toLowerCase()));
console.log(res);
similar to iota's, you don't need to create the extra array with Object.keys.
just loop/check every item inside the original array with the 'name' key.
you can also try to make it more reusable like below.
const someArrayOfObjects = [
{ id: '1', name: 'Something' },
{ id: '2', name: 'Another' },
{ id: '3', name: 'Lets do one more' },
];
const search = function (anyArray, searchTerm) {
return anyArray.filter((obj) => {
if (obj.name === searchTerm) {
return obj.name;
}
return false;
});
};
const case1 = search(someArrayOfObjects, 'Something');
console.log(case1);

Mocha Chai: Deep include an array of objects, but only have part of expected object

I'm using the assert syntax of chai for this.
I know that if I want to check an array of objects for a specific object, I can do this:
assert.deepInclude(
[
{ name: 'foo', id: 1 },
{ name: 'bar', id: 2 }
],
{ name: 'foo', id: 1 }
)
Which should pass.
But what if I only have 1 property in the object that I'm checking for...? Like this:
assert.deepInclude(
[
{ name: 'foo', id: 1 },
{ name: 'bar', id: 2 }
],
{ name: 'foo' }
)
I still want this to pass, but it's telling me it's failing because that exact object does not exist.
Using chai-subset this can be done pretty easily:
const chai = require('chai');
const chaiSubset = require('chai-subset');
chai.use(chaiSubset);
it('should contain subset', () => {
const actual = [
{ name: 'foo', id: 1 },
{ name: 'bar', id: 2 },
];
expect(actual).to.containSubset([{ name: 'foo' }]);
});
Afaik there's no way to do this with chai alone, but you could write your own function:
function containsPartialObj(arr, obj) {
return arr.some(entry => {
const keys = Object.keys(obj);
return keys.every(key => obj[key] === entry[key]);
});
}
it('should contain subset', () => {
const actual = [
{ name: 'foo', id: 1 },
{ name: 'bar', id: 2 },
];
expect(containsPartialObj(actual, { name: 'foo' })).to.be.true;
});

Concat array from Object from Array

I'm currently trying to retrieve a list of metadata stored as an array, inside an object, inside an array. Here's a better explanatory example:
[
{
name: 'test',
metadata: [
{
name: 'Author',
value: 'foo'
},
{
name: 'Creator',
value: 'foo'
}
]
},
{
name: 'otherTest',
metadata: [
{
name: 'Created',
value: 'foo'
},
{
name: 'Date',
value: 'foo'
}
]
},
{
name: 'finalTest'
}
]
Now, my objective is to retrieve a list of metadata (by their name) without redundancy. I think that .map() is the key to success but I can't find how to do it in a short way, actually my code is composed 2 for and 3 if, and I feel dirty to do that.
The expected input is: ['Author', 'Creator', 'Created', 'Date']
I'm developping in Typescript, if that can help for some function.
You can use reduce() and then map() to return array of names.
var data = [{"name":"test","metadata":[{"name":"Author","value":"foo"},{"name":"Creator","value":"foo"}]},{"name":"otherTest","metadata":[{"name":"Created","value":"foo"},{"name":"Date","value":"foo"}]},{"name":"finalTest"}]
var result = [...new Set(data.reduce(function(r, o) {
if (o.metadata) r = r.concat(o.metadata.map(e => e.name))
return r
}, []))];
console.log(result)
You could use Set for unique names.
var data = [{ name: 'test', metadata: [{ name: 'Author', value: 'foo' }, { name: 'Creator', value: 'foo' }] }, { name: 'otherTest', metadata: [{ name: 'Created', value: 'foo' }, { name: 'Date', value: 'foo' }] }, { name: 'finalTest' }],
names = new Set;
data.forEach(a => (a.metadata || []).forEach(m => names.add(m.name)));
console.log([...names]);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var data = [{"name":"test","metadata":[{"name":"Author","value":"foo"},{"name":"Creator","value":"foo"}]},{"name":"otherTest","metadata":[{"name":"Created","value":"foo"},{"name":"Date","value":"foo"}]},{"name":"finalTest"}]
data
.filter(function(obj){return obj.metadata != undefined})
.map(function(obj){return obj.metadata})
.reduce(function(a,b){return a.concat(b)},[])
.map(function(obj){return obj.name})
A hand to hand Array.prototype.reduce() and Array.prototype.map() should do it as follows;
var arr = [
{
name: 'test',
metadata: [
{
name: 'Author',
value: 'foo'
},
{
name: 'Creator',
value: 'foo'
}
]
},
{
name: 'otherTest',
metadata: [
{
name: 'Created',
value: 'foo'
},
{
name: 'Date',
value: 'foo'
}
]
},
{
name: 'finalTest'
}
];
result = arr.reduce((p,c) => c.metadata ? p.concat(c.metadata.map(e => e.name))
: p, []);
console.log(result);

How do I access nested objects properties in console.table()?

Lets say I have the following array of objects:
var data = [
{ id: 123, author: { id: 123 } },
{ id: 123, author: { id: 123 } }
];
How can I populate a column in console.table with the id property of the author object?
This does not seem to work: console.table(data, ['id', 'author.id']);
I'm not sure you can do it with nested properties.
You could use map to pull the data out into a better format and then console.table it:
const data = [
{ id: 123, author: { id: 123 } },
{ id: 123, author: { id: 123 } }
];
const out = data.map(obj => {
return {
id: obj.id,
authorId: obj.author.id
};
});
console.table(out);
Note: you cannot hide the index column.

Categories

Resources