Lodash - extending objects from array with nested object information - functional-programming

Im kinda getting into functional programming. My problem it is about how you can map information from nested data.
I have an array of companies.
[
{
_id: 123,
name: "Company"
},
{
_id: 789,
name: "Company"
}
];
I have an array of teams.
[
{
_id: 555,
name: "Team name A",
companyId: 123
},
{
_id: 666,
name: "Team name B",
companyId: 123
},
{
_id: 777,
name: "Team name C",
companyId: 789
}
];
I would like to put teams into company object for every company. The expect result would be:
[
{
_id: 123,
name: "Company",
teams: [
{
_id: 555,
name: "Team name A",
companyId: 123
},
{
_id: 666,
name: "Team name B",
companyId: 123
}
]
},
{
_id: 789,
name: "Company",
teams: [
{
_id: 777,
name: "Team name A",
companyId: 789
}
]
}
];
My solution has been to do it in several steps:
teamsGroupedByCompany = _.group(teams, 'companyId');
var findTeamsByCompanyId = function(groupedTeams, companyId){
return _.chain(groupedTeams)
.find(function(value, key){
return companyId == key;
})
.flatten()
.defaults([])
.value();
}
_.map(companies, function(company){
company.team = findTeamsByCompanyId(teamsGroupedByCompany, company._id);
})
Can it be done in a chaining way or I have to make higher order functions to deal with these kind of issues? Currently Im using lodash.
Here is the jsbin https://jsbin.com/buqiji/edit?js,console

The following code will do the trick.
companies = _.chain(companies)
.map(function(company) {
company.teams = _.filter(teams, {'companyId': company._id});
return company;
})
.value();
Here's a JsBin: https://jsbin.com/cudoheziyi/1/edit?js,console

My solution is similar to yours. I'm doing it in two steps as well.
You already have teamsGroupedByCompany so you can use that to assign teams to each company directly without having to do findTeamsByCompanyId. Here's my solution:
(assume companies and teams are already initialized)
const groupedTeams = _.groupBy(teams, 'companyId');
const combined = _.map(companies, c => {
c.teams = groupedTeams[c._id] || [];
return c;
});
http://jsbin.com/mixewo/1/edit?js,console

Related

Observable contains array of ID to call another observable

I have a data structure in firebase
{
"name": "Sample",
"category": ["123456", "789012"]
}
The array of category contains ID which refers to documents in another collection. I can get the above document as Observable. What I really what as the end result is the below data structure
{
"name": "Sample"
"category": [
{
"name": "Category 1"
},
{
"name": "Category 2"
}
]
}
How can I bring this data? I don't think switchMap works for this. If so, can someone give an example of that?
You can try using flatMap and forkJoin. FlatMap allows you to chain multiple async requests together and forkJoin allows you to wait for all observables to return a value before continuing.
And you could wright something like this:
var finalData;
firstRequest('sample').flatMap((data) => {
// assuming data = { name: "Sample", catagory: [ "23123", "31321", ... ] }
finalData = data;
var observables = [];
data.catagory.forEach((c) => {
observable.push(secondRequest(c));
});
return forkJoin(observables);
}).flatMap((results) => {
// assuming results is an array like [ { name: "Catagory 1", ... } ]
finalData.category = results;
return finalData;
});

Normalizr - Setting key of nested responses in results

I see in the normalizer examples that they have a name property "users" being returned on the results object:
{
result: { users: [ 1, 2 ] },
entities: {
users: {
'1': { id: 1 },
'2': { id: 2 }
}
}
}
I can't seem to figure out how to do this with the nested api response I'm getting. I have both a user and an address reducer that I am trying to pass the results of the normalized response into.
I have a JSON response that looks like this:
[
{
id: 1
first_name: First,
last_name: Last,
address: {
data: [
{
id: 1,
address_one: '123 Street Ave',
address_two: '',
city: 'Some City',
state: 'CA',
zip: '1234'
}
]
}
},
{
id: 1
first_name: First,
last_name: Last,
address: {
data: [
{
id: 2,
address_one: '123 Street Ave',
address_two: '',
city: 'Some City',
state: 'CA',
zip: '1234'
},
{
id: 3,
address_one: '321 Avenue Road',
address_two: 'Suite 101',
city: 'Some City',
state: 'CA',
zip: '1234'
}
]
}
}
]
My schema looks like this:
import { schema } from 'normalizr'
/**
* Addresses
*/
const address = new schema.Entity('addresses');
const arrayOfAddresses = new schema.Array(address);
/**
* User
*/
export const user = new schema.Entity('users', {
addresses: arrayOfAddresses
});
user.define({
addresses: {
data: arrayOfAddresses
}
})
export const arrayOfUsers = new schema.Array(user)
Doing: let result = normalize(data, schema.arrayOfUseres)
returns:
{
entities: {
addresses: /* The address data */
users: /* The users with an array of the address ids attached to them*/
},
result: [1, 2]
}
What I would really like is the result object to have both the users and the addresses in it:
{
entities: {
addresses: /* The address data */
users: /* The users with an array of the address ids attached to them */
},
result: {
addresses: [1, 2, 3]
users: [1, 2]
}
}
Is this possible? I've tried several variations of
import * as schema from './schema'
normalize(data, { users: [schema.user], addresses: [schema.address] }
But this just errors out and returns my data back into the result object.
Is this possible?
No. The result value is always set to match the top structure of your input data per the schema. If your input data doesn't have a first-level key addresses, it won't be possible.

REDUX - spread operator returning different result in comparison with concat method

I'm trying to learn what is the correct way to operate with Redux. I see around a lot of examples using the spread operator but I'm getting confused how to use it.
For instance, in the code below I have a very simple example where I fire in sequence the same action twice to simulate some products added to the cart.
the first action adds an array with two products while the second adds one product.
If I use concat to save the product in the state, I get the expected result of a cart array of three products:
"use strict"
import {createStore} from 'redux';
// REDUCERS
const reducer = function(state={cart:[]}, action) {
switch (action.type) {
case "ADD_TO_CART":
//HERE I ADD MY NEW ARRAY TO THE EXISTING ARRAY IN TEH STATE
let cart = [...state.cart].concat(action.payload);
return {...state, cart}
break;
}
return state
}
// CREATE STORE
const store = createStore(reducer);
// SUBSCRIBE
store.subscribe(function() {
console.log("current state is", store.getState());
})
// ACTIONS
store.dispatch({
type: "ADD_TO_CART",
payload:[ {
productId: 1,
name: "Product A",
price: 33.33
},
{
productId: 2,
name: "Product B",
price: 15.00
}]
})
store.dispatch({
type: "ADD_TO_CART",
payload:
[{
productId: 3,
name: "Product C",
price: 40.00
}]
})
Here is the result as I expected:
If instead I use the spread operator as below, I get a strange result where Redux creates two object, one containing two array with the product added from the first fired action and one object containing the third array/products:
"use strict"
import {createStore} from 'redux';
// REDUCERS
const reducer = function(state={cart:[]}, action) {
switch (action.type) {
case "ADD_TO_CART":
//HERE I ADD MY NEW ARRAY TO THE EXISTING ARRAY IN TEH STATE
return {...state, cart:[...state.cart, action.payload]}
break;
}
return state
}
// CREATE STORE
const store = createStore(reducer);
// SUBSCRIBE
store.subscribe(function() {
console.log("current state is", store.getState());
})
// ACTIONS
store.dispatch({
type: "ADD_TO_CART",
payload:[ {
productId: 1,
name: "Product A",
price: 33.33
},
{
productId: 2,
name: "Product B",
price: 15.00
}]
})
store.dispatch({
type: "ADD_TO_CART",
payload:
[{
productId: 3,
name: "Product C",
price: 40.00
}]
})
and this is the result from Chrome console:
The spread example doesn't work because the payload is an array:
payload:[ {
productId: 1,
name: "Product A",
price: 33.33
},
{
productId: 2,
name: "Product B",
price: 15.00
}]
})
The solution is to spread the payload as well:
state = {...state, cart: [...state.cart, ...action.payload] };
Example:
let state = { cart: [{ id: 1 }] };
const action = { payload: [{ id: 2}, { id: 3}] };
state = {...state, cart: [...state.cart, ...action.payload] };
console.log(state);
Note: since state.cart is an array, if you use Array#concat, you can simplify this:
[...state.cart].concat(action.payload);
By removing the spread and the wrapping array:
state.cart.concat(action.payload);

Using Firebase-util to resolve one to many relationship

I've been looking at the firebase util project at https://firebase.github.io/firebase-util/. I can't figure out if this is possible.
Say I have a data set like this:
{ clients:
{ client1: { groups: { group1: true, group2: true }, name: 'client1' },
client2: { groups: { group3: true, group4: true }, name: 'client2' } },
groups:
{ group1: { name: 'group1' },
group2: { name: 'group2' },
group3: { name: 'group3' },
group4: { name: 'group4' },
group5: { name: 'group5' } } }
I want to retrieve "client1" and resolve all the groups associated with client1, giving me back a dataset like this.
{ client1: { groups: { group1: { name: 'group1' }, group2: { name: 'group2' } }, name: 'client1' }
Is there a way to do this with Firebase-util?
There is an example on the website that is similar, where it is joining an account to a profile and then mapping the style to another reference point.
var ref = Firebase.util.join(
new Firebase('INSTANCE/account'),
{
ref: new Firebase('INSTANCE/profile'),
keyMap: {
name: 'name',
nick: 'nick',
style: new Firebase('INSTANCE/styles')
}
}
);
But, I'm not seeing how to do it with what I'm looking for.
Something like:
FirebaseUtil.join(
{ ref: new Firebase('INSTANCE/clients/client1'), keyMap: {name: 'name', groups: new Firebase('INSTANCE/groups')} }
)
Thanks for any hints!
You cannot use a nested list of groups as the keymap for clients/$client/groups. It must be a string pointing to exactly one reference.
The solution would be to retrieve the list separately from the meta data for the user:
var fb = new Firebase(URL);
getClients(fb.child('clients'), function(userId, data) {
console.log(userId, data);
});
function getGroupsForClient(clientId, callback) {
var indexRef = fb.child('clients/'+clientId+'/groups');
var groupsRef = fb.child('groups');
var intersectionRef = new Firebase.util.intersection(indexRef, groupsRef);
intersectionRef.once('value', callback);
}
function getClients(callback) {
fb.child('clients').on('child_added', function(snap) {
var userId = snap.name();
var userData = snap.val();
getGroupsForClient(userId, function(snap) {
userData.groups = snap.val();
callback(userId, userData);
});
});
}

meteor / minimongo working with nested data objects

I am trying to find the best way to do CRUD operations on a document that has a embedded document. [1 Business --> Many Services]
I have an business object that looks like this:
{
_id: "ZChonAoaksZ7kCvca",
name: "ABC",
description: "random description",
services: [
{ _id: "ZChonAoaksZ7kCvcf", service:"123", bufferEnd: 5 },
{ _id: "ZChonAoaksZ7kCvcg", service: "345", bufferEnd: 5 },
{ _id: "ZChonAoaksZ7kCvch", service: "567", bufferEnd: 5 },
]
}
Selections: i query my collection using:
collectionname.find({'services._id': 'ZChonAoaksZ7kCvcf'}).fetch()
as expected it simply returns the entire document. How can i return only the single sub document not the business object or an entire array? Is there a way just to get one item from the parent document such as:
{ _id: "ZChonAoaksZ7kCvcf", service:"123", bufferEnd: 5 }
Thanks
The 3 CRUD operations to edit Arrays within a MongoDB object from Meteor (care Update is the tricky one) are as follows:
Add a new item to document array:
Business.update({ _id: Business.findOne()._id }, {
$addToSet: {
services: {
_id: Random.id(),
service: $('#new_service_name').val(),
bufferEnd: $('#new_service_description').val(),
}
}
);
Edit an existing item in document array
var bus = Business.findOne().services;
var indexToUpdate = _.indexOf(_.pluck(bus, '_id'), Session.get('Service_selectedId'));
var modifier = { $set: {} };
modifier.$set["services." + indexToUpdate + ".service"] = $('#service_name').val();
modifier.$set["services." + indexToUpdate + ".description"] = $('#service_description').val();
Business.update(Business.findOne()._id, modifier);
Delete Operation
Business.update({ _id: Business.findOne()._id }, {
$pull: { services: { _id: Session.get('Service_selectedId') } }
});
You can filter the result after it's fetched:
var wholeDoc = Documents.findOne({'services._id': 'ZChonAoaksZ7kCvcf'});
var service = _.find(wholeDoc.services, function(service) {
return service._id === 'ZChonAoaksZ7kCvcf',
});

Resources