Functional way of joining two js object arrays based on common id - javascript

I am trying to acheive something similar to SQL table join,
in the most elegant (functional) way, preferably with underscore.js,
so no for loops please.
I need to merge objects from two different arrays, matched upon a common identifier.
For example, given:
var basic = [{
id: '1',
name: 'someName',
},
{...} ]
var ext= [{
id: '1',
job: 'someJob',
},
{...} ]
Result should be:
var combined = [{
id: '1',
name: 'someName',
job: 'someJob',
},
{...} ]
Thanks!

Map, findWhere and extend should do the trick:
var combined = _.map(basic, function(base){
return _.extend(base, _.findWhere(ext, { id: base.id} ));
});
Edit:
If performance is an issue create a hash of the extended values:
var extHash = _.reduce(ext, function(memo, extended, key){
memo[extended.id] = extended;
return memo;
}, {});
and use like so:
var combined = _.map(basic, function(base){
return _.extend(base, extHash[base.id]);
});
Fiddle

NO LOOP : http://jsfiddle.net/abdennour/h3hQt/2/
basic=basic.sort(function(a,b){return a.id-b.id});
ext=ext.sort(function(a,b){return a.id-b.id});
var combined=basic.map(function(e,i){return inherits(e,ext[i]) });
Known that ,inherits are as following:
function inherits(base, extension)
{
for ( var property in base )
{
extension[property] = base[property];
}
return extension ;
}

Related

Manipulating objects in an array

I am new to JavaScript and been struggling to combine objects in the same array.
Here is what I have:
var testList = [{
'taskList1': 'task1 for taskList1',
},
{
'taskList1': 'task2 for taskList1',
},
{
'taskList2': 'task1 for taskList2'
},
{
'taskList2': 'task2 for taskList2'
}]
The array below is what I really want to get from above:
var testList = [{
'taskList1': 'task1 for taskList1',
'taskList2': 'task1 for taskList2'
},
{
'taskList1': 'task2 for taskList1',
'taskList2': 'task2 for taskList2'
}]
Could anyone please help me to transform my current array into the one above?
Thank you in advance.
Your data structure is quite inefficient in this case. I'd suggest to make it better by having the same array of objects, but each object should contain itemName and belongsTo as a reference to any collection (in your case - a taskList) you may pass there.
Here's a solution to your problem with a more flexible data structure on Codepen - https://codepen.io/Inlesco/pen/dReYgd
I've also added the restructured array of tasks below as an example that's used in the Codepen above.
var testList = [{
item: 'task1',
belongsTo: 'taskList1'
},
{
item: 'task2',
belongsTo: 'taskList1'
},
{
item: 'task1',
belongsTo: 'taskList2'
},
{
item: 'task2',
belongsTo: 'taskList2'
}]
There are many ways to approach this problem. I've just added probably the simplest one.
You can use a for statement to regroup objects with the same taskList ID in one object.
And of course your need to use the right conditions for that.
But the best way is as #Denialos said, to modify your data structure.
Per my comments above to the question, your desired data structure appears to be inverted, or "inside out". Given a list of items, and a set of tasks for each item, I would expect the outer element to be the list, and the inner element to be the set of tasks.
Given that, given your (current) input data I would use:
function restructure(taskList) {
var result = {};
for (var i = 0, n = taskList.length; i < n; ++i) {
// read current item
var item = taskList[i];
var key = Object.keys(item)[0];
var value = item[key];
// update the output
result[key] = result[key] || [];
result[key].push(value);
}
return result;
}
with resulting output:
{
taskList1: [ 'task1 for taskList1', 'task2 for taskList1' ],
taskList2: [ 'task1 for taskList2', 'task2 for taskList2' ]
}
That said, your input data model is also somewhat malformed, with each array element having an unknown key. You should look at what's actually producing that data and fix that if possible.

Remove current object by index inside _.each with underscore

I have a nested object like so:
var depositOptions = {
0 : {name: 'Monthly'},
1 : {name : 'Standard'},
2 : {name: 'Full' },
3 : {name: 'Low' }
};
I need to remove the object where name = 'Standard' so I'm iterating over it using Underscore.js _.each until I find it, and storing the index.
_.each(depositOptions, function(option, i) {
if (option.name === 'Standard') {
// delete the object at index i
console.log(i)
}
});
So I want to remove the option when it finds standard - what is the easiest way to do this?
1) Remove it inside the _.each loop, when standard is found? (Therefore no need for the index)
2) Store the index of the object I want to remove, and then delete it after I've finished iterating? (This I am finding difficult because how do I get the index outside of the loop?)
3) Start creating a new array as I iterate over depositOptions so that a new array is built without standard included
When I say I want to delete it, I want it completely gone. So not just undefined like using delete does.
Use _.filter or _.reject instead. It wouldn't be clear to use _.each. Notice that these return a new array.
var newArray = _.filter(depositOptions, function(option) {
return option.name !== 'Standard';
});
Also it looks like you have an object, not an array. Here is an array:
var depositOptions = [
{name: 'Monthly'},
{name : 'Standard'},
{name: 'Full' },
{name: 'Low' }
];
You can use the _.without function to eliminate the key which you don't need
var depositOptions = {
0 : {name: 'Monthly'},
1 : {name : 'Standard'},
2 : {name: 'Full' },
3 : {name: 'Low' }
};
depositOptions = _.without(depositOptions, _.findWhere(depositOptions, {
name: 'Standard'
}));
console.log(depositOptions);
Hope this helps!.
If you want to return new object with filtered properties you can use Object.keys() and reduce() in plain javascript.
var depositOptions = {
0 : {name: 'Monthly'},
1 : {name : 'Standard'},
2 : {name: 'Full' },
3 : {name: 'Low' }
};
var result = Object.keys(depositOptions).reduce(function(r, k) {
if (depositOptions[k].name != 'Standard') r[k] = depositOptions[k]
return r;
}, {})
console.log(result)
According to me the best way to do that using underscore is _reject.
Usage -
_.reject(depositOptions, function(val){ return val.name == 'Standard'; });
Option 3 is definitely not an option because you using exclusion method, where you are trying to fetch all other elements apart from the one you want & make use of the element you want. Too dirty for your implementation!
Option 2 is better than option 3 but not ideal because you have found the element & your still wasting your time iterating over others
Option 1 with a tweak is the best solution from the options given - small tweak includes add a break statement within the if statement & after you store the element. This way your just iterating till you find the element.
You have 2 options, you either mutate your original object or you create a new object without the unwanted key-value pair. All examples are in plain ES6, but you can easily use lodash methods. They will exclude all matches not just the first match.
Mutation. Uses for..of and Object.keys
const depositOptions = {
0: {
name: 'Monthly'
},
1: {
name: 'Standard'
},
2: {
name: 'Full'
},
3: {
name: 'Low'
}
};
for (let key of Object.keys(depositOptions)) {
if (depositOptions[key].name === 'Standard') {
delete depositOptions[key];
}
}
console.log(depositOptions);
Create new object without the reference to the unwanted object.
const depositOptions = {
0: {
name: 'Monthly'
},
1: {
name: 'Standard'
},
2: {
name: 'Full'
},
3: {
name: 'Low'
}
};
const depositOptionsFiltered = {};
for (let key of Object.keys(depositOptions)) {
if (depositOptions[key].name !== 'Standard') {
depositOptionsFiltered[key] = depositOptions[key];
}
}
console.log(depositOptionsFiltered);
You may even want a new object and the key-value object to be a copy of the original rather than references to them. In this case you can perform a shallow copy. Uses Object.assign
const depositOptions = {
0: {
name: 'Monthly'
},
1: {
name: 'Standard'
},
2: {
name: 'Full'
},
3: {
name: 'Low'
}
};
const depositOptionsFiltered = {};
for (let key of Object.keys(depositOptions)) {
if (depositOptions[key].name !== 'Standard') {
depositOptionsFiltered[key] = Object.assign({}, depositOptions[key]);
}
}
console.log(depositOptionsFiltered);

How to delete a object in a list? Javascript nodejs and underscore

Hello I am new to Javascript and NodeJS, but I need to delete a object in a array with a boolean in the list. There are 3 friends in the list and 2 friends with isEvil = true; So i need to delete 2 friends and the output must be 1.
This is what i tried.
MyTry 1:
_.each(user.friends, function(friend) {
if(friend.isEvil){
delete friend;
}
});
console.log(user.friends.length); //output is 3
If I do this it will delete all the properties but there is still a empty object there: MyTry 2:
_.each(user.friends, function(friend) {
if(friend.isEvil){
delete f.property1;
delete f.property2;
}
});
console.log(user.friends.length); //output is 3
And the last one i tried is:
_.each(user.friends, function(friend, key) {
if(friend.isEvil){
delete user.friends[key];
}
});
console.log(user.friends.length); //output is 3
just use the _.filter(list, predicate, [context]) function provided with underscore:
_.filter(user.friends, function(friend){
return !friend.isEvil;
});
for more info: http://underscorejs.org/#filter
Filter the users friend like
user.friends.filter(function (friend) {
return !friend.isEvil;
});
var frieds = [{ name: 1, isEvil: false }, { name: 2, isEvil: true }, { name: 3, isEvil: true }];
var notEvil = friends.filter(function (friend) {
return !friend.isEvil;
});
console.log(notEvil);
To get rid of evil friends you could use underscore's reject function:
var niceFriends = _.reject(friends, 'isEvil');
var friends = [
{ id: 'mickey', isEvil: false },
{ id: 'donald', isEvil: true },
{ id: 'minnie', isEvil: false }
];
var niceFriends = _.reject(friends, 'isEvil');
document.getElementById('result').textContent = JSON.stringify(niceFriends);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore.js"></script>
<p>
<pre id="result"></pre>
</p>
Delete operator leaves your array with undefined on the place of removed element (see delete). I believe you should use Splice method to remove elements.
Or you might just use built in Underscores methods to filter your array (e.g. filter), like:
user.friends = _.filter(user.friends, function(item) {
return !item.isEvil;
};
try this,basically you iterate though you array and splice all unwanted objects:
for(var i in user.friends){
if(user.friends[i].isEvil){
user.splice(i,1)
}
}

Create tags object from array in mongo collection for Select2 options

I have a mongo collection with a array field called 'tags'. What I want to do is create a single object that stores all of the various tags with a label and value. The end result should be an object I can use in a Select2 field in a Meteor application to create the results options. I have gotten close, but all of my solutions have not worked and are super ugly (read: not functional javascript)
Here is a sample document:
{
"_id": "sjkjladlj",
"title": "Coldplay is Cool",
"tags": ["music", "yuppie"]
}
Now the end result I would like is:
[
{
value: "music",
label: "music"
},
{
value: "yuppies",
label: "yuppies"
},
{
value: "Some tag from another doc"
label: "Some tag from another doc"
}
]
Any ideas?
Here is the closest I have gotten.
options: function() {
tagsArray = [];
ca = Notes.find({}, {tags: 1}).fetch();
ca.forEach(function(it) {
result = {};
result = it.tags;
tagsArray.push(result);
});
console.log(tagsArray);
return tagsArray;
}
}
you can try with aggregation pipeline like this
db.colleaction.aggregate([{$project:{_id:0,tags:1}},{$unwind:"$tags"},{$project:{"value":"$tags","lable":"$tags"}}])
Update. As soon as I posted I realized I simply need to add a inner loop. Its ugly, but it works.
options: function() {
tagsArray = [];
ca = Notes.find({}, {tags: 1}).fetch();
ca.forEach(function(it) {
result = {};
result = it.tags;
result.forEach(function(child){
inner = {};
inner.value = child;
inner.label = child;
tagsArray.push(inner);
});
});
console.log(tagsArray);
return tagsArray;
}

underscore.js groupBy sub-array

I have some data
var data = [
{type: "A", role:[1,2]},
{type: "B", role:[2]},
{type: "C", role:[2,3]},
{type: "D", role:[3]}
];
I'm trying to sortBy 'role' using underscore.js
var groups = _(data).groupBy(function(el){
return el.role
}
);
Is there a easy way to get reapeated data like this
1: {
{type:"A" ...}
},
2: {
{type:"A" ...},
{type:"B" ...},
... etc
not like this http://jsbin.com/IzEwUkim/2/edit
You can, as usual, do it by hand with _.reduce, something like this:
var groups = _(data).reduce(function(memo, o) {
_(o.role).each(function(i) {
memo[i] = memo[i] || [ ];
memo[i].push(o);
});
return memo;
}, { });
Demo: http://jsfiddle.net/ambiguous/HDE3c/
You could use a plain for loop instead of _.each to iterate over the roles of course.
_.groupBy isn't going to work here as the function/key is always a single value so I think you're stuck unwrapping the role arrays by hand.
I know you asked for underscore, but here's native js:
var map = {};
data.forEach(function(obj){
obj.role.forEach(function(roleVal){
(map[roleVal] = map[roleVal] || []).push(obj);
});
});
console.log(map);

Categories

Resources