Merging linked Data in Array in Javascript - javascript

I have a simple task of rearranging a couple of Arrays in a JSON, so ractive.js can handle it better. But I got carried away a bit, and the outcome was not particularly satisfactory.
An example of my initial Array:
[{
"_id": 1,
"type": "person",
"Name": "Hans",
"WorksFor": ["3", "4"],
}, {
"_id": 2,
"type": "person",
"Name": "Michael",
"WorksFor": ["3"],
}, {
"_id": 3,
"type": "department",
"Name": "Marketing"
}, {
"_id": 4,
"type": "department",
"Name": "Sales"
}, {
"_id": 5,
"type": "person",
"Name": "Chris",
"WorksFor": [],
}]
So with a given Department I wanted a method in ractive to give me all Persons who work in this Department (with a list of Departments they work for). Something like:
[{
"_id": 1,
"type": "person",
"Name": "Hans",
"WorksFor": ["3", "4"],
"Readable": ["Marketing", "Sales"]
}, {
"_id": 2,
"type": "person",
"Name": "Michael",
"WorksFor": ["3"],
"Readable": ["Sales"]
}]
The function that somehow came to life was similar to this:
function imsorryforthis() {
let output = [];
let tempdocs = this.get('docs'); //as this happens in a ractive method,
//"this.get" is neccesary for binding
for (var i = 0; i < tempdocs.length; i++) {
if (_.contains(tempdocs[i].WorksFor, givenDepartment)) { //I used underscore.js here
let collectedDepartmentData = [];
if (tempdocs[i].WorksFor.length > 0) {
for (var f = 0; f < tempdocs[i].WorksFor.length; f++) {
for (var g = 0; g < tempdocs.length; g++) {
if (tempdocs[i].WorksFor[f] == tempdocs[g]._id) {
let actualDepartmentData = {};
actualDepartmentData = tempdocs[g];
collectedDepartmentData.push(actualDepartmentData);
}
}
}
}
tempdocs[i].Readable = collectedDepartmentData;
output.push(tempdocs[i]);
}
}
return output;
}
I've put it in a Fiddle as well to make it better readable.
Due to the fact that somehow this monstrosity does work (I was astonished myself), it feels like scratching your left ear with your right hand over your head (while being constantly shouted at by a group of desperate mathematicians).
Maybe anybody knows a more presentable and smarter approach (or a way to compile JavaScript so this never sees the light of day again).

Construct a map department_id => department_name first:
let departments = {};
for (let x of data) {
if (x.type === 'department') {
departments[x._id] = x.Name;
}
}
Then, iterate over Persons and populate Readable arrays from that map:
for (let x of data) {
if (x.type === 'person') {
x.Readable = x.WorksFor.map(w => departments[w]);
}
}
Finally, extract Persons for the specific Department:
personsInSales = data.filter(x =>
x.type === 'person' && x.WorksFor.includes('3'));

Firstly, your data structure does not have a good design. You should not be returning person and department in the same array. If possible, try to redesign the initial data structure to make it more modular, by separating out the people and department into separate structures. However if you are stuck with this same data structure, you can write the code a little better. Please find the code below. Hope it helps!
function mapPeopleDepartment() {
var deptMap = {},peopleList = [];
//Iterate through the initialArray and separate out the department into a hashmap deptMap and people into a new peopleList
for(var i=0; i < initArray.length; i++) {
var obj = initArray[i];
obj.type == "department" ? deptMap[obj._id] = obj.Name : peopleList.push(obj);
}
//Iterate through the peopleList to map the WorksFor array to a Readable array
for(var i=0; i < peopleList.length; i++) {
var person = peopleList[i];
person.Readable = _.map(person.WorksFor, function(dept){return deptMap[dept]});
}
return peopleList;
}

Related

Getting unique result out of two lists of objects angular

Hi I have two list of objects that i am trying to get unique objects in new list in my angular 4 component.
IList1 l1=[ {"_id":'a1', "name": "john"},
_id":'a2', "name": "adam"},
_id":'a3', "name": "jenny"}];
IList1 l2=[ {"_id":'a1', "name": "john"},
_id":'b5', "name": "joe"},
_id":'a3', "name": "jenny"}];
I am trying to write a code so that I can get the unique item in new list so
IList1 result=[_id":'b5', "name": "joe"}]
I am following is my code to get the result list
var len1=this.l1.length;
var len2=this.l2.lenth;
Ilist1 result=[];
for(var i=0; i<l1; i++)
{
for(var y=0; y<l2; y++)
{
if(this.l1[i]._id !== this.l2[y]._id)
this.result.push(this.l2[y])
}
}
Code above gives the result which is apposite of what I want basically it spits out the common objects in both lists. I tried to change !== to == still not giving the expected result.
Please let me know how I can fix it to get unique record into the result list. Thanks
This is what is happening with your current code:
This is what your code is doing in english:
For each element of list l1 (for(var i=0; i<l1; i++));
go through list l2 (for(var y=0; y<l2; y++));
if the element of l1 is not on all elements of l2 (if(this.l1[i]._id !== this.l2[y]._id))
add it to the list (this.result.push(this.l2[y]))
So what's wrong there is step 3, which translated into execution we get on the first iteration of the part for loop:
l1[0] !== l2[0] ("a2" !== "a1") // true- result = []
l1[0] !== l2[1] ("a2" !== "b5") // true - result = [{"_id":'b5', "name": "joe"}]
l1[0] !== l2[2] ("a2" !== "a3") // true - result = [{"_id":'b5', "name": "joe"}, {"_id":'a3', "name": "jenny"}]
On the second iteration:
l1[1] !== l2[0] ("a1" !== "a1") // false - result = [{"_id":'b5', "name": "joe"}, {"_id":'a3', "name": "jenny"}, {"_id":'a1', "name": "john"}]
l1[1] !== l2[1] ("a1" !== "b5") // true - result = [{"_id":'b5', "name": "joe"}, {"_id":'a3', "name": "jenny"}, {"_id":'a1', "name": "john"},{"_id":'b5', "name": "joe"}]
l1[1] !== l2[2] ("a1" !== "a3") // true - result = [{"_id":'b5', "name": "joe"}, {"_id":'a3', "name": "jenny"}, {"_id":'a1', "name": "john"},{"_id":'b5', "name": "joe"},{"_id":'b5', "name": "joe"}]
So you can see that your condition is not ok because all other elements that are not equal to the current element under test is going to be added to your list.
So to make the logic simpler let's just concat the 2 arrays and apply the same logic as before only going through the same concatenated list twice and using a boolean that tells you if the element is a duplicate or not:
var l1 = [{
"_id": 'a1',
"name": "john"
},
{
"_id": 'a2',
"name": "adam"
},
{
"_id": 'a3',
"name": "jenny"
}
];
var l2 = [{
"_id": 'a1',
"name": "john"
},
{
"_id": 'b5',
"name": "joe"
},
{
"_id": 'a3',
"name": "jenny"
}
];
var result = [];
var joinedArray = l1.concat(l2);
for (var i = 0; i < joinedArray.length; i++) {
var isDuplicate = false;
for (var y = 0; y < joinedArray.length; y++) {
if (i != y && this.joinedArray[i]._id === this.joinedArray[y]._id)
isDuplicate = true;
}
if (!isDuplicate)
this.result.push(joinedArray[i]);
}
console.log(result)
This solution is not the most efficient or the most logic, but it should be easier to understand since it's closer to your original logic.
I would probably count the objects in a hash keyed to the id. Then filter() & map() that count object for items with a count of 1.
let l1=[ {"_id":'a1', "name": "john"},{"_id":'a2', "name": "adam"},{"_id":'a3', "name": "jenny"}];
let l2=[ {"_id":'a1', "name": "john"},{"_id":'b5', "name": "joe"},{"_id":'a3', "name": "jenny"}];
// make an object that counts the items
// only need to store the last item since you're looking for uniques
let counts = [...l1, ...l2].reduce((obj, item) =>{
if (obj[item._id]) obj[item._id].count++
else obj[item._id] = {count:1, object: item}
return obj
}, {})
// filter that object by count and return the objects
let filtered = Object.values(counts).filter(item => item.count === 1).map(item => item.object)
console.log(filtered)

How to add a new key to multiple indices of an array of objects?

I've got an array of three people. I want to add a new key to multiple objects at once based on an array of indices. Clearly my attempt at using multiple indices doesn't work but I can't seem to find the correct approach.
var array = [
{
"name": "Tom",
},
{
"name": "Dick",
},
{
"name": "Harry",
}
];
array[0,1].title = "Manager";
array[2].title = "Staff";
console.log(array);
Which returns this:
[
{
"name": "Tom",
},
{
"name": "Dick",
"title": "Manager"
},
{
"name": "Harry",
"title": "Staff"
}
]
But I'd like it to return this.
[
{
"name": "Tom",
"title": "Manager"
},
{
"name": "Dick",
"title": "Manager"
},
{
"name": "Harry",
"title": "Staff"
}
]
You cannot use multiple keys by using any separator in arrays.
Wrong: array[x, y]
Correct: array[x] and array[y]
In your case, it will be array[0].title = array[1].title = "manager";
1st method::
array[0].title = "Manager";
array[1].title = "Manager";
array[2].title = "Staff";
array[0,1] will not work.
2nd method::
for(var i=0;i<array.length;i++) {
var msg = "Manager";
if(i===2) {
msg = "Staff"
}
array[i].title = msg
}
You can use a helper function like this
function setMultiple(array, key, indexes, value)
{
for(i in array.length)
{
if(indexes.indexOf(i)>=0){
array[i][key] = value;
}
}
}
And then
setMultiple(array, "title", [0,1], "Manager");
Try this: `
for (var i=0; var<= array.length; i++){
array[i].title = "manager";
}`
Or you can change it around so var is less than or equal to any n range of keys in the index.
EDIT: instead make var <= 1. The point is to make for loops for the range of indices you want to change the title to.
Assuming that you have a bigger set of array objects.
var array = [
{
"name": "Tom",
},
{
"name": "Dick",
},
{
"name": "Harry",
},
.
.
.
];
Create an object for the new keys you want to add like so:
let newKeys = {
'Manager': [0,2],
'Staff': [1]
}
Now you can add more such titles here with the required indexes.
with that, you can do something like:
function addCustomProperty(array, newKeys, newProp) {
for (let key in newKeys) {
array.forEach((el, index) => {
if (key.indexOf(index) > -1) { // if the array corresponding to
el[newProp] = key // the key has the current array object
} // index, then add the key to the
}) // object.
}
return array
}
let someVar = addCustomProperty(array, newKeys, 'title')

JSON data transformation from table-like format to JSON?

I have data that's in this format:
{
"columns": [
{
"values": [
{
"data": [
"Project Name",
"Owner",
"Creation Date",
"Completed Tasks"
]
}
]
}
],
"rows": [
{
"values": [
{
"data": [
"My Project 1",
"Franklin",
"7/1/2015",
"387"
]
}
]
},
{
"values": [
{
"data": [
"My Project 2",
"Beth",
"7/12/2015",
"402"
]
}
]
}
]
}
Is there some super short/easy way I can format it like so:
{
"projects": [
{
"projectName": "My Project 1",
"owner": "Franklin",
"creationDate": "7/1/2015",
"completedTasks": "387"
},
{
"projectName": "My Project 2",
"owner": "Beth",
"creationDate": "7/12/2015",
"completedTasks": "402"
}
]
}
I've already got the column name translation code:
r = s.replace(/\%/g, 'Perc')
.replace(/^[0-9A-Z]/g, function (x) {
return x.toLowerCase();
}).replace(/[\(\)\s]/g, '');
Before I dive into this with a bunch of forEach loops, I was wondering if there was a super quick way to transform this. I'm open to using libraries such as Underscore.
function translate(str) {
return str.replace(/\%/g, 'Perc')
.replace(/^[0-9A-Z]/g, function (x) {
return x.toLowerCase();
})
.replace(/[\(\)\s]/g, '');
}
function newFormat(obj) {
// grab the column names
var colNames = obj.columns[0].values[0].data;
// create a new temporary array
var out = [];
var rows = obj.rows;
// loop over the rows
rows.forEach(function (row) {
var record = row.values[0].data;
// create a new object, loop over the existing array elements
// and add them to the object using the column names as keys
var newRec = {};
for (var i = 0, l = record.length; i < l; i++) {
newRec[translate(colNames[i])] = record[i];
}
// push the new object to the array
out.push(newRec);
});
// return the final object
return { projects: out };
}
DEMO
There is no easy way, and this is really not that complex of an operation, even using for loops. I don't know why you would want to use regex to do this.
I would start with reading out the column values into a numerically indexed array.
So something like:
var sourceData = JSON.parse(yourJSONstring);
var columns = sourceData.columns[0].values[0].data;
Now you have a convenient way to start building your desired object. You can use the columns array created above to provide property key labels in your final object.
var sourceRows = sourceData.rows;
var finalData = {
"projects": []
};
// iterate through rows and write to object
for (i = 0; i < sourceRows.length; i++) {
var sourceRow = sourceRows[i].values.data;
// load data from row in finalData object
for (j = 0; j < sourceRow.length; j++) {
finalData.projects[i][columns[j]] = sourceRow[j];
}
}
That should do the trick for you.

Get an object in array by one of its fields

Sorry I'm kind of new to JS; I have an array of object; how can I get the name of the object which has the key "user_key3" and obviously without having a loop and have a condition.
arr = [{
"name": "user1",
"key": "user_key1"
},{
"name": "user3",
"key": "user_key3"
},{
"name": "user2",
"key": "user_key2"
}]
Please let me know if you need more clarification
Thanks
You can do it the functional way, like this
var name;
arr.forEach(function(currentObject) {
if (currentObject.key === "user_key3") {
name = currentObject.name;
}
});
If you want to short-circuit on the first match, you can use Array.prototype.some, like this
var name;
arr.some(function(currentObject) {
if (currentObject.key === "user_key3") {
name = currentObject.name;
return true;
}
return false;
});
The OP had mentioned obviously without having a loop and have a condition. I would do it as below:
arr = [{
"name": "user1",
"key": "user_key1"
},{
"name": "user3",
"key": "user_key3"
},{
"name": "user2",
"key": "user_key2"
}];
var keyValMap = arr.map(function(n) { return n.key } );
var arrIndex = keyValMap.indexOf('user_key3');
alert(arr[arrIndex].name);
Fiddle
You'll have to iterate and check for the key
var user_name;
for (var i=0; i<arr.length; i++) {
if ( arr[i].key === 'user_key3' ) {
user_name = arr[i].name;
break;
}
}
FIDDLE
You've edited the question to include
obviously without having a loop and have a condition
but a loop and a condition is by far the most efficient and cross-browser way to do this, so why would you "obviously" not want this ?
An inefficient yet concise solution would be
var keyarr = arr.map(function(x) { return x.key } );
//keyarr is list of keys
var index=keyarr.indexOf("user_key3");
//arr[index] is your answer. Index will be -1 if the key doesn't exist
In general, finding an item that satisfies some arbitrary property in an array requires you to loop over the array:
function find(arr, name) {
for (var i=0; i<arr.length; i++) {
if ( arr[i].key === name ) {
return arr[i];
}
}
}
Then to find it,
var obj = find(arr, 'user_key3');
Using more functional solutions to find the item is fine too, but you still end up looping in some way.
However, if you are doing lookups by key, then an array of key-value pairs is not the best data structure. I would suggest using an object directly:
var map = {
'user_key1': 'user1',
'user_key2': 'user2',
'user_key3': 'user3'
}
Then lookup is simply:
map['user_key3'];
Try this - underscore.js
For Your Example -
_.where(arr, {key: "user_key3"});
You cannot do such thing with Objects in Javascript. Though here you have a combination of callbacks and loop:
arr = [{
"name": "user1",
"key": "user_key1"
},{
"name": "user3",
"key": "user_key3"
},{
"name": "user2",
"key": "user_key2"
}];
arr.forEach(function(elme){
for(var g in elme)
{
if(elme[g] == 'user_key3')
{
console.log("Found the value: "+g+" : "+elme[g]);
};
}
});

complex grouping and merging array as properties

I wish to put an array into other's array as proproties by matching their common properties. I want jobDetails's uId to match with job's uId. Possible?
var job = [{
"uId": 1
}, {
"uId": 2
}]
var jobDetails = [{
"uId": 1,
"salary": 5000
}, {
"uId": 2,
"salary": 5000
}]
is it possible to produce something like
var job = [{
"uId": 1,
"salary": [{
"uId": 1,
"salary": 5000
}]
}, {
"uId": 2,
"salary": [{
"uId": 2,
"salary": 5000
}]
}];
You may try something like this: http://jqversion.com/#!/XWFtbQb
for (var i = 0; i < job.length; i++) {
for (var j = 0; j < jobDetails.length; j++) {
if (job[i].uId == jobDetails[j].uId) {
job[i].salary = jobDetails[j];
}
}
}
console.log(job);
This is not a pure javascript solution, but I like to use underscore.js for this kind of typical actions on collections:
http://jsfiddle.net/FPwq7/
var finalCollection = [];
_.each(job, function(model){
var obj = _.findWhere(jobDetails, {uId: model.uId});
_.extend(model, {'salaray': obj});
finalCollection.push(model);
});
console.log(finalCollection);
I found that this Javascript utility belt takes care of some heavy lifting, and it makes the code a bit more pleasant to read than reading dry loops.
Yes possible , you need to play with both json objects
var array = [];
var object = {}
$.each( job, function ( k , kal ) {
$.each( jobDetails , function ( i , val) {
object.uId = i;
object.salary = val;
});
array.push(object);
});
console.log(JSON.stringify(array));

Categories