I have 2 array objects in Angular JS that I wish to merge (overlap/combine) the matching ones.
For example, the Array 1 is like this:
[
{"id":1,"name":"Adam"},
{"id":2,"name":"Smith"},
{"id":3,"name":"Eve"},
{"id":4,"name":"Gary"},
]
Array 2 is like this:
[
{"id":1,"name":"Adam", "checked":true},
{"id":3,"name":"Eve", "checked":true},
]
I want the resulting array after merging to become this:
[
{"id":1,"name":"Adam", "checked":true},
{"id":2,"name":"Smith"},
{"id":3,"name":"Eve", "checked":true},
{"id":4,"name":"Gary"},
]
Is that possible? I have tried angular's array_merge and array_extend like this:
angular.merge([], $scope.array1, $scope.array2);
angular.extend([], $scope.array1, $scope.array2);
But the above method overlap the first 2 objects in array and doesn't merge them based on matching data. Is having a foreach loop the only solution for this?
Can someone guide me here please?
Not sure if this find of merge is supported by AngularJS. I've made a snippet which does exactly the same:
function merge(array1, array2) {
var ids = [];
var merge_obj = [];
array1.map(function(ele) {
if (!(ids.indexOf(ele.id) > -1)) {
ids.push(ele.id);
merge_obj.push(ele);
}
});
array2.map(function(ele) {
var index = ids.indexOf(ele.id);
if (!( index > -1)) {
ids.push(ele.id);
merge_obj.push(ele);
}else{
merge_obj[index] = ele;
}
});
console.log(merge_obj);
}
var array1 = [{
"id": 1,
"name": "Adam"
}, {
"id": 2,
"name": "Smith"
}, {
"id": 3,
"name": "Eve"
}, {
"id": 4,
"name": "Gary"
}, ]
var array2 = [{
"id": 1,
"name": "Adam",
"checked": true
}, {
"id": 3,
"name": "Eve",
"checked": true
}, ];
merge(array1, array2);
Genuinely, extend in Angular works with object instead of array. But we can do small trick in your case. Here is another solution.
// a1, a2 is your arrays
// This is to convert array to object with key is id and value is the array item itself
var a1_ = a1.reduce(function(obj, value) {
obj[value.id] = value;
return obj;
}, {});
var a2_ = a2.reduce(function(obj, value) {
obj[value.id] = value;
return obj;
}, {});
// Then use extend with those two converted objects
var result = angular.extend([], a1_, a2_).splice(1)
Notes:
For compatibility, reduce may not work.
The after array will replace the previous one. This is because of implementation of extend in Angular.
Related
I am trying to figure out an easy way to convert an array of objects to an object
I have an array of objects that looks like this:
[
{
"id": "-LP9_kAbqnsQwXq0oGDT",
"value": Object {
"date": 1541482236000,
"title": "First",
},
},
.... more objects here
]
And id like to convert it to an object with the timestamps as the keys, and arrays of objects corresponding to that date. If that key already exists, then add the object to the corresponding array associated with that key
{
1541482236000:
[{
"id": "-LP9_kAbqnsQwXq0oGDT",
"value": Object {
"date": 1541482236000,
"title": "First",
},
},
{
"id": "-LP9_kAbqnsQwXqZZZZ",
"value": Object {
"date": 1541482236000,
"title": "Some other title",
},
},
.... more objects here
],
1541482236001:
[{
"id": "-LP9_kAbqnsQ1234",
"value": Object {
"date": 1541482236001,
"title": "Another title",
},
},
.... more objects here
]
}
I was able to achieve something similar using reduce. However it does not handle adding objects to the array when their key already exists.
calendarReminders = action.value.reduce((obj, reminder) => {
dateKey = moment(reminder.value.date).format('YYYY-MM-DD')
obj[dateKey] = [reminder]
return obj;
}, {});
How can I do this?
You just need to check whether the object is already a key and if not add it with the value of an array. Then you can just push() into it:
let arr = [{"id": "-LP9_kAbqnsQwXq0oGDT","value": {"date": 1541482236000,"title": "First",},},{"id": "SomID","value": {"date": 1541482236000,"title": "Some other title",},},{"id": "A different ID","value": {"date": 1541482236001,"title": "A third title",},}]
let calendarReminders = arr.reduce((obj, reminder) => {
(obj[reminder.value.date] || (obj[reminder.value.date] = [])).push(reminder)
return obj;
}, {});
console.log(calendarReminders)
If you want to set the keys to a different format with moment, you should be able to do that without changing the basic idea.
Please test the below code!
First you iterate through your array of data,
if your result object/dictionary already has the key then you just add the current item
otherwise you make the key and set the value
const data = [];
let result = {};
for (const item of data) {
const key = item.value.date;
if (result.hasOwnProperty(key)) {
const prevData = result[key];
result[key] = [...prevData, item];
} else {
result[key] = [item];
}
}
I have 3 different jsons, I need to extrapolate some data from each and create a new json with it. The three jsons have an id identifier in common, a unique identifier, so We could use that as a match since they are actually three different big jsons.
On json one we have "id":"265", on two and three "article_id":"265", so these can be the reference point when we loop.
I never worked with json this way so I wouldn't know how to approach it. I have put jQuery and JS as tags as they're what I know best.
1
{
"id":"265",
"title":"Battle of Gettysburg",
"page_id":"4849",
"language_id":"en",
"original_time":"July 1\u20133, 1863"
}
2
{
"id":"185",
"original_name":"United States",
"country_id":"24",
"article_id":"265"
}
3
{
"id":"73",
"month":"July",
"year":"1863",
"suffix":"",
"article_id":"265"
}
So the end result I am looking for is a single json exactly like this, we take id and title as objects from json 1, then we grab original_name from json two and year object from json three and we'll have:
{
"id":"265",
"title":"Battle of Gettysburg",
"original_name":"United States",
"year":"1863"
}
NOTE
The json above are just examples, in reality they are three huge lists, what I could do (manually), is to join them in order to have a single json.
There is some terminology confusion here; based on your comments you could be asking one of two very different questions. Fortunately one of them is very simple to answer so let's do both.
(I am handwaving past the details of loading json strings into the browser and converting them into javascript objects.)
If you have three objects
...then this is just a matter of plucking out the fields you need individually when constructing an output object:
var in1 = {
"id": "265",
"title": "Battle of Gettysburg",
"page_id": "4849",
"language_id": "en",
"original_time": "July 1\u20133, 1863"
};
var in2 = {
"id": "185",
"original_name": "United States",
"country_id": "24",
"article_id": "265"
}
var in3 = {
"id": "73",
"month": "July",
"year": "1863",
"suffix": "",
"article_id": "265"
}
// construct a new object using the selected fields
// from each object in1, in2, or in3:
var out = {
id: in1.id,
title: in1.title,
original_name: in2.original_name,
year: in3.year
}
console.log(out);
If you have three lists of objects:
...in this case it's a lot more complicated (and a lot more interesting). In this case you would need to match fields from the objects in each list which share the same IDs.
The following is definitely not the most efficient or memory-conserving way to do this; I've spread things out to (hopefully) make it easier to follow what it's doing.
I'm making two assumptions:
within each list, all IDs are unique (meaning you won't have two objects with the same ID in one JSON file)
Every ID will appear in all three lists (meaning you don't need to handle missing fields in output)
/* Again handwaving past loading JSON strings and parsing
them into javascript objects, we'll just start with
three arrays: */
var input1 = [{
"id": "265",
"title": "Battle of Gettysburg",
"page_id": "4849",
"language_id": "en",
"original_time": "July 1\u20133, 1863"
},
{
"id": "1",
"title": "Foo",
"page_id": "123",
"language_id": "en",
"original_time": "July 1\u20133, 1863"
}
];
var input2 = [{
"id": "1",
"original_name": "Bar",
"country_id": "24",
"article_id": "265"
},
{
"id": "265",
"original_name": "United States",
"country_id": "24",
"article_id": "265"
}
]
var input3 = [{
"id": "1",
"month": "July",
"year": "Baz",
"suffix": "",
"article_id": "265"
},
{
"id": "265",
"month": "July",
"year": "1863",
"suffix": "",
"article_id": "265"
}
]
/* It would be much easier to find corresponding IDs
across these arrays if they weren't arrays. We'll
start by converting them into objects keyed by the
item ids: */
var convertArray = function(arr) {
var output = {};
arr.forEach(function(o) {
output[o.id] = o;
});
return output;
}
var obj1 = convertArray(input1);
var obj2 = convertArray(input2);
var obj3 = convertArray(input3);
/* Now if we need to find (say) the object with id "foo", we don't
need to search the whole array, but can just use `obj1["foo"]` or
`obj1.foo`.
The last step is to iterate over the list of IDs and repeatedly
do basically the same thing as in the "if you have three objects"
part above. The only difference is that we need to access the
object with the same ID in each of the input lists: */
var constructOutput = function(in1, in2, in3) {
var output = []; // we'll be outputting a list of objects again.
// step through every ID (assuming in1 contains all of them):
Object.keys(in1).forEach(function(id) {
var obj = {
id: id,
title: in1[id].title,
original_name: in2[id].original_name,
year: in3[id].year
}
output.push(obj);
});
return output;
}
var final = constructOutput(obj1, obj2, obj3)
console.log(final)
Essentially what you have to do is mimic a SQL JOIN using JavaScript objects:
Use JSON.parse() on all three JSON collections to turn them into arrays of objects.
Iterate through JSON 1 objects; for each object...
Iterate through JSON 2 objects, testing if article ID matches the ID from JSON 1 that we are iterating over. Save this object.
Iterate through JSON 3 objects, testing if ID matches the ID of the object we found from JSON 2. Save this object.
After you have all three objects, make a new object literal that contains only the fields you want:
{
Id: obj1.id,
Title: obj1.title,
Original_name: obj2.original_name,
Year: obj3.year
}
Should you want to combine n number of JSON objects, e.g. a list of objects you can take a functional approach and utilise reduce + filter.
const data = [{
"id":"265",
"title":"Battle of Gettysburg",
"page_id":"4849",
"language_id":"en",
"original_time":"July 1\u20133, 1863"
},
{
"id":"185",
"original_name":"United States",
"country_id":"24",
"article_id":"265"
},
{
"id":"73",
"month":"July",
"year":"1863",
"suffix":"",
"article_id":"265"
}];
const final = data.reduce((accu, { id, title }, index, array) => {
// Find any related objects
const matches = array.filter(data => data.article_id === id);
if (matches.length) {
// Flatten them for ease of access. Duplicate keys will override.
const flat = matches.reduce((arr, item) => ({ ...arr, ...item }), [])
// Return new object
return accu.concat({
...flat,
id,
title,
});
}
return accu;
}, []);
console.log(final, '<<')
// Witness
document.getElementById('results').innerHTML = JSON.stringify(final);
<div id="results" style="font-family: Courier; font-size 14px; color: #fff; background: #000; padding: 20px; max-width: 80vw;"></div>
Edited*
Maybe this is what you need?
let arrPages = [{
"id":"265",
"title":"Battle of Gettysburg",
"page_id":"4849",
"language_id":"en",
"original_time":"July 1\u20133, 1863"
}];
let arrArticles = [{
"id":"185",
"original_name":"United States",
"country_id":"24",
"article_id":"265"
},
{
"id":"73",
"month":"July",
"year":"1863",
"suffix":"",
"article_id":"265"
}];
let getResult = (arrInput, arrCompare) => {
let joinedItems = [];
arrInput.forEach(item => {
let newItem = { id: item.id, title: item.title };
arrCompare.forEach(subItem => {
if(subItem.article_id !== undefined && subItem.article_id === item.id){
if(subItem.original_name !== undefined)
newItem.original_name = subItem.original_name;
if(subItem.year !== undefined)
newItem.year = subItem.year;
}
});
joinedItems.push(newItem);
});
return joinedItems;
};
let result = getResult(arrPages, arrArticles);
console.log(result);
In the first part of the code i create a var that has the json data.
To solve the problema i create 2 functions, the order of the creation dosen't metter, the first function getJSONData() take the json data as parameter and return a object filtered by the keys defined in the array keys. The secound function just check if the current key is present in the array of keys, this function could be replaced by the jQuery.inArray() method.
// JSON data
var json = [{
"id":"265",
"title":"Battle of Gettysburg",
"page_id":"4849",
"language_id":"en",
"original_time":"July 1\u20133, 1863"
},
{
"id":"185",
"original_name":"United States",
"country_id":"24",
"article_id":"265"
},
{
"id":"73",
"month":"July",
"year":"1863",
"suffix":"",
"article_id":"265"
}]
// keys that i want
var keys = ["title", "original_name", "year"];
// var that will have the filtered data
var newJSON = getJSONData(json);
console.log(JSON.stringify(newJSON))
// this is the main function of the code
// here we iterate in the json creating a new object that has all the tags definid in the keys array
function getJSONData(arrayJSON){
var JSONFiltered = {};
for(var i in arrayJSON){
for(var key in arrayJSON[i]){
if(hasElement(key)){
JSONFiltered[key] = arrayJSON[i][key];
}
}
}
return JSONFiltered;
}
// this function is used to check a key is present in the array of keys
function hasElement(key){
for(var elem in keys){
if(keys[elem] == key) return true;
}
return false;
}
I have two arrays
$scope.tags = [{ "id": 1, "name": "python" }, { "id": 2, "name": "NodeJs" }, { "id": 3, "name": "git" }]
Other one is
$scope.skillsInterested = [1,2];
What is want to do ?
How can i map the above arrays and print only names of the id's in$scope.skillsInterested
I want to print names in first array only the id's present in second.
I have tried this after getting several answers
var tag_map = {};
for (var x = 0; x < $scope.tags.length; x++) {
tag_map[$scope.tags[x]['id']] = $scope.tags[x]['name'];
}
$scope.skillsInts = $scope.skillsInterested.map(function(x) {
return tag_map[x]
On running console.log
console.log("Result", tag_map);
It sometimes give result sometimes it gives 'map' of undefined.
TypeError: Cannot read property 'map' of undefined
at controllers.js:141
at angular.js:16383
at m.$eval (angular.js:17682)
at m.$digest (angular.js:17495)
at m.$apply (angular.js:17790)
at l (angular.js:11831)
at J (angular.js:12033)
at XMLHttpRequest.t.onload (angular.js:11966)
Thanks in advance.
Make a map of your data that looks like this:
var tagMap = { 1: "python", 2: "NodeJs" /* etc. */ };
You can do this by looping over your tags and adding a new property to an object. reduce lets you do this without creating any extra variables.
Then, you can select names from your newly created object using the [] notation: tagMap[1] returns "pyhton".
var tags = [{ "id": 1, "name": "python" }, { "id": 2, "name": "NodeJs" }, { "id": 3, "name": "git" }]
var selectedExpTags = [1,2];
// Make a map for `id: name`
var tagMap = tags.reduce(function(map, tag) {
map[tag.id] = tag.name;
return map;
}, {});
// Quickly select names from the map:
var selectedNames = selectedExpTags.map(function(id) {
return tagMap[id];
});
console.log(selectedNames);
Using this approach, you minimise the iterations over your data. The creation of the map loops over the tags once. Creating the array with names, loops over the selected tags once. So, roughly, the "loop count" is tags.length + selectedTags.length. If you would use an indexOf based approach, your loop count would be tags.length * selectedTags.length.
Use the filter function for first, and then check the id's existnent then map the names from the array.
var first = [{ "id": 1, "name": "python" }, { "id": 2, "name": "NodeJs" }, { "id": 3, "name": "git" }];
var selectedExpTags = [1,2];
var names = first.filter(item => selectedExpTags.some(id => item.id === id)).map(item => item.name);
console.log(names);
You can loop over $scope.selectedExpTags and get a list of all names. You can use array.find if you want first value only.
Sample
var first = [
{ "id": 1, "name": "python" },
{ "id": 2, "name": "NodeJs" },
{ "id": 3, "name": "git" }];
var selectedExpTags = [1,2];
var names = selectedExpTags.map(x=> first.find( y=> y.id === x ).name )
console.log(names);
$scope.newArray = []; // If you need a new array to work with
angular.forEach($scope.tags, function(tag){
$scope.selectedExpTags.forEach(function(selectedTag){
if(selectedTag == tag.id){
//tag.hide = false; // - If you want to update the current array
$scope.newArray.push(tag);
}
// else{ // - If you want to update the current array
// tag.hide = true;
// }
})
})
Lodash is more efficient than angular for manipulating data.
Is there any way to compare two arrays and push to an empty array if the condition is met?
Say I have an array of objects. I need to loop through the array of objects, get a ID; then compare that ID to a different array. Then if they match push a value in that array to an empty array?
Array 1:
[{
"addon_service": {
"id": "f6f28cb5-78ad-4ec7-896d-16462b8202fd",
"name": "papertrail"
},
"app": {
"id": "199a1f26-b8e2-43f6-9bab-6e7a6c685ec2",
"name": "mdda-mobiledocdelivery-stg"
}
}]
Array 2
[{
"app": {
"id": "199a1f26-b8e2-43f6-9bab-6e7a6c685ec2"
},
"stage": "staging",
}]
I need to match Array 1 app.ID to Array 2 app.id. If they match check what stage the app is in (staging, development or production). Then push Array 1 addon_service.name to either a staging develpment or
production array depending on what stage the application is in. I'm thinking its simple just cant get my head around it.
I think this is a poorly worded question.
You could use a hash table for lookup and for the stage and use an object for collecting the matches.
var array1 = [{ "addon_service": { "id": "f6f28cb5-78ad-4ec7-896d-16462b8202fd", "name": "papertrail" }, "app": { "id": "199a1f26-b8e2-43f6-9bab-6e7a6c685ec2", "name": "mdda-mobiledocdelivery-stg" } }],
array2 = [{ "app": { "id": "199a1f26-b8e2-43f6-9bab-6e7a6c685ec2" }, "stage": "staging", }],
hash = Object.create(null),
result = {};
array2.forEach(function (a) {
hash[a.app.id] = a.stage;
});
array1.forEach(function (a) {
if (hash[a.app.id]) {
result[hash[a.app.id]] = result[hash[a.app.id]] || [];
result[hash[a.app.id]].push(a.addon_service.name);
}
})
console.log(result);
I think this will do it.
$.each(app1, function(key, value){
$.each(app2, function(k, v){
if(value.app.id == v.app.id){// find apps with the same `id`
if(v[v.stage]){// check if the `stage` array already exists.
v[v.stage].push(value.addon_service)
}else{
v[v.stage] = [value.addon_service];
}
}
});
});
Where app1 is the first array in your question and app2 the second one.
I push in values from JSON into a several arrays using Underscore, but I want to eliminate any repeated values if there are any, either during push or after. How could I do this?
JSON
looks = [{
"id": "look1",
"products": ["hbeu50271385", "hbeu50274296", "hbeu50272359", "hbeu50272802"]
}, {
"id": "look2",
"products": [
"hbeu50274106", "hbeu50273647", "hbeu50274754", "hbeu50274063", "hbeu50274911", "hbeu50274106", "hbeu50240022", "hbeu50271944"
]
}, {
"id": "look3",
"products": [
"hbeu50272935", "hbeu50274426", "hbeu50271624", "hbeu50274762", "hbeu50275366", "hbeu50274433", "hbeu50262002", "hbeu50272364", "hbeu50272359"
]
}
.......
]
JS (Underscore)
var productArrays = [];
_.each(looks, function(look) {
var productArray = [];
_.each(look.products, function(product) {
productArray.push(product.replace(/_.*/, ''))
})
productArrays.push(productArray);
});
There are couple ways
1.Use _.uniq
_.uniq(productArray);
2.Use _.indexOf before push to productArray
Example
For array's content be unique, how about using _.uniq?
Or just check existence of value before really push it.
function uniquePush(arr, valueToPush) {
if(arr.indexOf(valueToPush) == -1) {
arr.push(valueToPush)
}
}