Advanced example of rxjs5 flattening - javascript

I have similar data structure to this:
let userInfo = [
{
id: 'id1',
users: [
{
name: 'userName1',
job: 'userJob',
},
{
name: 'userName2',
job: 'userJob',
}
]
},
{
id: 'id2',
users: [
{
name: 'userName3',
job: 'userJob',
},
{
name: 'userName4',
job: 'userJob',
}
]
}
]
Users are
I expected new flattened user stream with RxJS5:
{
parent: id, // parent id, where users[] come from...
name: 'userName'
job: 'userJob'
}
What is the clean functional way to archive this? Thank you...

The easiest approach would just use concatMap() to merge the internal Observable created with Rx.Observable.from with the list of users updated with their parent id.
let userInfo = [
{
id: 'id1',
users: [
{
name: 'userName1',
job: 'userJob',
},
{
name: 'userName2',
job: 'userJob',
}
]
},
{...}
];
let source = Rx.Observable.from(userInfo)
.concatMap(group => {
return Rx.Observable.from(group['users'])
.map(user => {
user['parent'] = group.id;
return user;
});
});
source.subscribe(
res => console.log(res)
);
See live demo: https://jsbin.com/miximas/2/edit?js,console
There're a lot of similar question already:
refactor fat arrow nested rxjs stream
Merge subarrays using Observables
RxJS: JSON data with an array, processing each item further in the stream

Related

How to render two different arrays data into a single array of objects?

In my react state, I have data coming in into two different arrays and I would like my component to put both arrays data in a single array. This is the data format that I have for both arrays:
data: {
array1: [
{
name: "John Doe"
},
{
name: "Bob Williams"
}
]
array2: [
{
name: "Clark Kent"
},
{
name: "Bruce Wayne"
}
]
}
How can I make my code return data so the end result looks like the defined below:
_.map(Object.values(this.state.data), (item,key) => {
return _.map(item, (data) => {
return {
name: data.name
}
})
})
Desired output:
data: [
{ name: "John Doe" },
{ name: "Bob Williams" },
{ name: "Clark Kent" },
{ name: "Bruce Wayne" }
]
You can use Spread operator ... to merge 2 arrays into one.
const data = {array1:[{name:"John Doe"},{name:"Bob Williams"}],array2:[{name:"Clark Kent"},{name:"Bruce Wayne"}]};;
const result = {data: [...data.array1, ...data.array2]};
console.log(result);
Use Array.prototype.flat()
const data = Object.values(this.state.data).flat();
Object.values(this.state.data).reduce((p, c) => p.concat(c), []);

Reduce an array of objects based on an object array property

I need help from some JavaScript/NodeJS experts. I'm using the v14 of NodeJS.
I would like to create a function that takes as an input this kind of array (let's say that it's a list of teams):
[
{
name: 'IT',
tags: ['Department', 'Section', 'Organizational']
},
{
name: 'Male',
tags: ['Gender', 'Organizational']
},
{
name: 'Foo',
tags: []
}
]
... and returns as an output a list of teams by tag name like this:
// Output:
{
Department: 'IT',
Section: 'IT',
Organizational: 'IT, Male',
Gender: 'Male'
}
The order of object keys as well as the order of the team names don't matter. I can do this quite easily with a few lines of code, but I'm quite sure that it would be easy to do using a few of our magic map(), reduce() functions and other utilities like spreading.
Anybody can help me achieve this in an optimized way?
Thank you!
Try this:
const organization = [
{
name: 'IT',
tags: ['Department', 'Section', 'Organizational']
},
{
name: 'Male',
tags: ['Gender', 'Organizational']
},
{
name: 'Foo',
tags: []
}
]
const result = organization.reduce((acum, current) => {
current.tags.forEach(tag => {
if(acum[tag]) acum[tag] = `${acum[tag]}, ${current.name}`
else acum[tag] = current.name
})
return acum
},{})
console.log(result)

how do i create a new object based off an array and another object?

I am trying to create a new object based off an existing array. I want to create a new object that show below
{ jack: 'jack', content: 'ocean'},
{ marie: 'marie', content: 'pond'},
{ james: 'james', content: 'fish biscuit'},
{paul: 'paul', content: 'cake'}
const words = ['jack','marie','james','paul']
const myUsers = [
{ name: 'jack', likes: 'ocean' },
{ name: 'marie', likes: 'pond' },
{ name: 'james', likes: 'fish biscuits' },
{ name: 'paul', likes: 'cake' }
]
const usersByLikes = words.map(word => {
const container = {};
container[word] = myUsers.map(user => user.name);
container.content = myUsers[0].likes;
return container;
})
I am not getting the correct object, but instead it returns a list.
[ { jack: [ 'shark', 'turtle', 'otter' ], content: 'ocean'}, { marie: [ 'shark', 'turtle', 'otter' ], content: 'ocean' },
{ james: [ 'shark', 'turtle', 'otter' ], content: 'ocean' },
{ paul: [ 'shark', 'turtle', 'otter' ], content: 'ocean'} ]
What is the role of words array? I think the below code will work.
const result = myUsers.map(user => ({
[user.name]: user.name,
content: user.likes
}));
console.log('result', result);
In case, if want to filter the users in word array then below solution will work for you.
const result = myUsers.filter(user => {
if (words.includes(user.name)) {
return ({
[user.name]: user.name,
content: user.likes
})
}
return false;
});
You can achieve your need with a single loop.
The answer #aravindan-venkatesan gave should give you the result you are looking for. However important to consider:
When using .map() javascript returns an array of the same length, with whatever transformations you told it to inside map().
If you want to create a brand new object, of your own construction. Try using .reduce(). This allows you to set an input variable, i.e: object, array or string.
Then loop over, and return exactly what you want, not a mapped version of the old array.
See here for more details:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

Merge the object using typescript

In my angular application i am having the data as follows,
forEachArrayOne = [
{ id: 1, name: "userOne" },
{ id: 2, name: "userTwo" },
{ id: 3, name: "userThree" }
]
forEachArrayTwo = [
{ id: 1, name: "userFour" },
{ id: 2, name: "userFive" },
{ id: 3, name: "userSix" }
]
newObj: any = {};
ngOnInit() {
this.forEachArrayOne.forEach(element => {
this.newObj = { titleOne: "objectOne", dataOne: this.forEachArrayOne };
})
this.forEachArrayTwo.forEach(element => {
this.newObj = { titleTwo: "objectTwo", dataTwo: this.forEachArrayTwo };
})
console.log({ ...this.newObj, ...this.newObj });
}
In my real application, the above is the structure so kindly help me to achieve the expected result in the same way..
The working demo https://stackblitz.com/edit/angular-gyched which has the above structure.
Here console.log(this.newObj) gives the last object,
titleTwo: "ObjectTwo",
dataTwo:
[
{ id: 1, name: "userFour" },
{ id: 2, name: "userFive" },
{ id: 3, name: "userSix" }
]
but i want to combine both and need the result exactly like the below..
{
titleOne: "objectOne",
dataOne:
[
{ id: 1, name: "userOne" },
{ id: 2, name: "userTwo" },
{ id: 3, name: "userThree" }
],
titleTwo: "ObjectTwo",
dataTwo:
[
{ id: 1, name: "userFour" },
{ id: 2, name: "userFive" },
{ id: 3, name: "userSix" }
]
}
Kindly help me to achieve the above result.. If i am wrong in anywhere kindly correct with the working example please..
You're assigning both values to this.newObj, so it just overwrites the first object.
Also, there is no need for your loop. It doesn't add anything.
Instead, you can do:
this.newObjA = { titleOne: "objectOne", dataOne: this.forEachArrayOne };
this.newObjB = { titleTwo: "objectTwo", dataTwo: this.forEachArrayTwo };
console.log({ ...this.newObjA, ...this.newObjB });
**
EDIT **
Having spoken to you regarding your requirements, I can see a different solution.
Before calling componentData, you need to make sure you have the full data. To do this, we can use forkJoin to join the benchmark requests, and the project requests into one Observable. We can then subscribe to that Observable to get the results for both.
The code would look something like this:
createComponent() {
let benchmarks, projects;
let form = this.productBenchMarkingForm[0];
if (form.benchmarking && form.project) {
benchmarks = form.benchmarking.filter(x => x.optionsUrl)
.map(element => this.getOptions(element));
projects = form.project.filter(x => x.optionsUrl)
.map(element => this.getOptions(element));
forkJoin(
forkJoin(benchmarks), // Join all the benchmark requests into 1 Observable
forkJoin(projects) // Join all the project requests into 1 Observable
).subscribe(res => {
this.componentData({ component: NgiProductComponent, inputs: { config: AppConfig, injectData: { action: "add", titleProject: "project", dataProject: this.productBenchMarkingForm[0] } } });
})
}
}
getOptions(element) {
return this.appService.getRest(element.optionsUrl).pipe(
map((res: any) => {
this.dataForOptions = res.data;
element.options = res.data;
return element;
})
)
}
Here is an example in Stackblitz that logs the data to the console

Filtering a list of objects by a child array in Angular 2

I have data that looks like this
[
{id: 1234,
Name: 'John',
Tags: ['tag1', 'tag2']
},
{id: 1235,
Name: 'Mike',
Tags: ['tag1', 'tag3']
}
]
I want to be able to type into a search bar and filter the data to search for related tags. There was a built in filter pipe for this in angular 1 but it looks like it has been removed in angular 2. I've been looking into custom pipes but the only way I can think to do it is with a nested loop that loops over all of the objects then loops through the tags. Am I thinking about this wrong. Is there an easier way to do this or a built in function that would work for this?
You can just use normal javascript APIs to get that behaviour:
data = [
{id: 1234,
Name: 'John',
Tags: ['tag1', 'tag2']
},
{id: 1235,
Name: 'Mike',
Tags: ['tag1', 'tag3']
}
];
filterDataByTag(searchTerm: string) {
// filter the data array, based on some condition
return this.data.filter(item => {
// only include an item in the filtered results
// if that item's Tags property includes the searchTerm
// includes is a built in array method in ES2016
return item.Tags.includes(searchTerm);
});
}
In my example, i'm harding coding the data, but you can adjust to suit your situation. The key point is the function returns a filtered list of the data based on the searchTerm, so you can just call this method each time you want to refresh your filtered list (for eg on the input event of your search field)
You should reorganize data into a reverse index store :
export interface StoreData {
Tag: string;
Peoples: People[] = [];
}
const store: StoreData[] = [];
export interface People {
id: number;
Name: string;
Tags: string[];
}
loadPeopleStore(peoples: People) {
peoples.forEach(p => {
p.Tags.forEach(t => {
let storeData = store.filter(d => d.Tag === t);
if(storeData.length == 1) {
storeData[0].Peoples.push(p);
} else {
store.push({Tag: t, Peoples[p]});
}
}
}
}
initYourPipe() {
let peoples: People[] = [
{id: 1234,
Name: 'John',
Tags: ['tag1', 'tag2']
},
{id: 1235,
Name: 'Mike',
Tags: ['tag1', 'tag3']
}
]
this.loadPeopleStore(peoples);
}

Categories

Resources