How to merge multiple objects in javascript? [closed] - javascript

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have the following JSON object array in javascript :
[{ "AuthorName" : "Abc", "BookName" : "book-1" }]
[{ "AuthorName" : "Abc", "BookName" : "book-2" }]
[{ "AuthorName" : "Abc", "BookName" : "book-3" }]
[{ "AuthorName" : "Abc", "BookName" : "book-4" }]
Now I want to create a single JSON object from the above JSON objects array. The newly created single JSON object contains 2 properties: AuthorName and BooKName array. I am trying to achieve something like this:
{ "AuthorName" : "Abc", "Book" : ['book-1', 'book-2', 'book-3', 'book-4'] }
Now my question is, how can I achive this efficiently and with writing minimum code in javascript?

var obj = {};
for(var i=0; i<myArray.length; i++) {
if(obj[myArray[i].AuthorName] == null)
obj[myArray[i].AuthorName] = [];
obj[myArray[i].AuthorName].push(myArray[i].BookName)
}

Hopefully, this will help:
var bookSort = function (bookarray) {
var i,
book,
authorArray = [],
il = bookarray.length,
j,
jl,
authorInArray;
for (i = 0; i < il; i++) {
authorInArray= false;
jl = authorArray.length;
book = bookArray[i];
for (j = 0; j < jl; j++) {
if (book.AuthorName = authorArray[j].AuthorName) {
authorInArray= true;
authorArray[j].BookName.push(book.BookName);
break;
}
}
if (!authorInArray) {
authorArray.push({AuthorName: book.AuthorName, BookName: [book.BookName]});
}
}
return authorArray;
};

Seems like you need a function that combines multiple objects.
If you create a general purpose function that does this, you can reuse it. I would discourage you from creating a solution with things like authorArray etc hard coded into it.
Let's create a function that takes multiple objects and combines them. Let's keep it simple and assume the objects look like the ones from your question. In other words the objects to combine will simply be a flat list of name value pairs. The values will either be a string or an array of strings.
jsFiddle Demo
// A function that combines multiple object.
// The original objects are made of name value pairs where the values are strings.
// If - for a key - the values are the same, the value is kept
// If - for a kye - the values are different, and array is created and the values pushed to it
// after this all new values are added to the array if not already there.
var combineObjects = function() {
// see how many object are to be combined
var length = arguments.length,
i,
// Create a new empty object that will be returned
newObject = {},
objectIn,
prop,
temp,
ii,
alreadyExists;
// Go through all passed in object... combinging them
for (i = 0; i < length; ++i) {
objectIn = arguments[i];
for (prop in objectIn) {
if (objectIn.hasOwnProperty(prop)) {
// Check if the prop exisits
if (newObject[prop]) {
// Check if the prop is a single or multiple (array)
if (Object.prototype.toString.call( newObject[prop] ) === '[object Array]') {
// Multiple
// Check if element is in array
alreadyExists = false;
for (ii = 0; ii < newObject[prop].length; ++ii) {
if (newObject[prop][ii] === objectIn[prop]) {
alreadyExists = true;
break;
}
}
if (! alreadyExists) {
newObject[prop].push(objectIn[prop]);
}
} else {
// Single
if (newObject[prop] !== objectIn[prop]) {
temp = newObject[prop];
newObject[prop] = [temp, objectIn[prop]];
}
}
} else {
newObject[prop] = objectIn[prop];
}
}
}
}
// Alert for testing
alert(JSON.stringify(newObject));
return newObject;
};

It looks like those are just four seperate arrays containing objects.
If you put those four seperate arrays inside another array so they can be iterated, like so:
var a = [
[{ AuthorName : 'Abc', BookName : 'book-1'}],
[{ AuthorName : 'Abc', BookName : 'book-2'}],
[{ AuthorName : 'Abc', BookName : 'book-3'}],
[{ AuthorName : 'Abc', BookName : 'book-4'}]
];
I'd just do:
var new_array = [],
temp_obj = {};
$.each(a, function(i,e) {
var author = e[0].AuthorName,
book = e[0].BookName;
temp_obj[author] ? temp_obj[author].push(book) : temp_obj[author] = [book];
});
$.each(temp_obj, function(author,books) {
var obj = {AuthorName: author, BookName : books};
new_array.push(obj);
});
//new_array is now = [{ AuthorName : 'Abc', BookName : ['book-1', 'book-2', 'book-3', 'book-4'] }]
FIDDLE
and it would sort out more authors and books etc. aswell ?
PROOF

Not sure what you are after. Maybe this will work for you:
var books = [
[{ "AuthorName" : "Abc", "BookName" : "book-1" }],
[{ "AuthorName" : "Abc", "BookName" : "book-2" }],
[{ "AuthorName" : "Abc", "BookName" : "book-3" }],
[{ "AuthorName" : "Abc", "BookName" : "book-4" }]
];
// First book in the array
var first = books[0][0];
// Add BookName property to the first book object (BookNames would be a better name)
first.BookName = books.map(function(book, n) {
return book[0].BookName;
});
/*
Or, use jQuery.map if you got a older browser that don't support the Array.map function
$.map(books, function(book, n) {
return book[0].BookName;
});
*/
// first is now:
{ AuthorName : "Abc", BookName : ['book-1', 'book-2', 'book-3', 'book-4'] }

Related

Merging some values in Javascript Object

I'm wondering if it is possible to merge certain values in an object to an array/object. I'm currently developing a module to create a quotation and everytime a value is changed in the form I want to send the form data to the server (async ajax call) to auto save the quotation. I'm getting the form data (by react hook form's getValues() function) as shown on the screenshot below:
As seen on the screenshot there are multiple lines[0] and lines[1 ] entries that are all seperate entries. To send it to the server I need the data as following:
client_id: "686",
condition: "7",
...
lines: {
0: {
amount: 1,
description: 'Test line 1',
price: 100,
},
1: {
amount: 1,
description: 'Test line 2',
price: 200,
}
},
...
Anyone here who knows how I can accomplish to convert my data into something like the example above? Thanks in advance!
Here is a solution for any key with dots.
Expanded the solution for the square brackets:
var source = {
id:12,
cond:"true",
'lines[0].amount':"1",
'lines[0].description':"a1",
'lines[1].amount':"1",
'lines[1].description':"a1",
};
var target = {};
for (var key in source) {
if (source.hasOwnProperty(key)) {
set(target, key, source[key])
}
}
function set(obj, path, value) {
obj = typeof obj === 'object' ? obj : {};
path = path.replace("[",".").replace("]","")
var keys = Array.isArray(path) ? path : path.split('.');
var curStep = obj;
for (var i = 0; i < keys.length - 1; i++) {
var key = keys[i];
if (!curStep[key] && !Object.prototype.hasOwnProperty.call(curStep, key)){
var nextKey = keys[i+1];
var useArray = /^\+?(0|[1-9]\d*)$/.test(nextKey);
curStep[key] = useArray ? [] : {};
}
curStep = curStep[key];
}
var finalStep = keys[keys.length - 1];
curStep[finalStep] = value;
};
In general for merging values, folks should check out Object.assign().
In this particular case, to go from the second screenshot to the first, you might have used a loop, something like:
let newObject = {};
for (let lineIndex=0; lineIndex<lines.length; lineIndex++) {
for (let fieldIndex=0; fieldIndex<Object.keys(lines[lineIndex]).length; fieldIndex++) {
let newKey = "lines["+lineIndex+"]."+fieldIndex<Object.keys(lines[lineIndex])[fieldIndex];
newObject[newKey] = lines[lineIndex][Object.keys(lines[lineIndex])[fieldIndex]]
}
}
To go the other way, you would basically do the inverse. The details of the best way to do this would depend on how predictable your set of fields are and if you know the length. Otherwise you could make guesses by iterating over the keys in the flatter object and doing tests like if(Object.keys(flatterObject)[index].startsWith("lines[")).
Yes, there is a way to do what you want, but before any of that I'd suggest a deeper dive on the library you're using. Here's how to handle arrays in React Hook Form: https://react-hook-form.com/advanced-usage#FieldArrays
After "fixing" that, if you still want lines: {} to be an object, not an array, then a simple array reduce can build that up for you.

find a value inside array of JSON object [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I get below Array of JSON objects from JSP
"Titles":[
{
"Book3" : "BULLETIN 3"
}
,
{
"Book1" : "BULLETIN 1"
}
,
{
"Book2" : "BULLETIN 2"
}
]
On JS side, it is parsed and I see an array with 3 objects.
Now, I want to find/identify a value when I pass String key.
For e.g. when I pass "Book2" I should get value "BULLETIN 2".
Can someone help me identify the approach?
Try this
var data = {
"Titles": [{
"Book3": "BULLETIN 3"
}, {
"Book1": "BULLETIN 1"
}, {
"Book2": "BULLETIN 2"
}]
};
function getValueByKey(key, data) {
var i, len = data.length;
for (i = 0; i < len; i++) {
if (data[i] && data[i].hasOwnProperty(key)) {
return data[i][key];
}
}
return -1;
}
console.log(getValueByKey('Book2', data.Titles));
Having:
var x = [{
"Book3" : "BULLETIN 3"
}, {
"Book1" : "BULLETIN 1"
}, {
"Book2" : "BULLETIN 2"
}];
and
var key = "Book1";
You can get the value using:
x.filter(function(value) {
return value.hasOwnProperty(key); // Get only elements, which have such a key
}).shift()[key]; // Get actual value of first element with such a key
Notice that it'll throw an exception, if object doesn't have such a key defined.
Also, if there are more objects with such key, this returns the first one only. If you need to get all values from objects with such key, you can do:
x.filter(function(value) {
return value.hasOwnProperty(key); // Get only elements, which have such a key
}).map(function(value) {
return value[key]; // Extract the values only
});
This will give you an array containing the appropriate values only.
Additionally, if you're using jQuery, you can use grep instead of filter:
jQuery.grep(x, function(value) {
return value.hasOwnProperty(key);
}) /* and so on */;
To achieve this, you have to loop through the array elements's keys and test if the given key exists in the array, if so get its value:
var jsonTitles = [
{ "Book3" : "BULLETIN 3" },
{ "Book1" : "BULLETIN 1" },
{ "Book2" : "BULLETIN 2" }
]
function getValue(key, array) {
for (var el in array) {
if (array[el].hasOwnProperty(key)) {
return array[el][key];
}
}
}
alert(getValue("Book1", jsonTitles));
We use element[key] where element is array[el] to get the value of the given key.
Let's create a function to get an object in an array for that, that takes two arguments: the array and the key of the property you want to get:
function getObjectInArray(arr, key) {
for (var i = 0; i < arr.length; i++) {
if (arr[i].hasOwnProperty(key)) return arr[i][key];
}
}
This loops through each object looking for that specific key.
Solution: Now you could do something like getObjectInArray(titlesJSONArray, "Book2") and it should return "BULLETIN 2".
var titlesJSONArray = [ { "Book3": "BULLETIN 3" }, ... ]; // and so on
var book2 = getObjectInArray(titlesJSONArray, "Book2"); // => "BULLETIN 2"
For such array/collection manipulation in Javascript I would suggest you to use underscorejs library. It provides functions that, to me, make everything much more simple. In your case:
function find_value(array, key) {
// find will run the provided function for every object in array
var obj_found = _.find(array, function(obj) {
// keys returns the keys inside an object
// so if the key of currently examined object
// is what we are looking for, return the obj
if (_.keys(obj)[0] === key) {
return obj;
}
});
// if an object with such key was found return its value
if (obj_found) {
return obj_found[key];
} else {
return null;
}
}
Here is a working fiddle of what I am suggesting.

How can I filter an array to remove duplicate entries?

I have an array of data returned from my server. From this array I need to
get an array of Topics and an array of SubTopics:
var data =
[
{"topicId":1,"subTopicId":1,"topicName":"J","subTopicName":" Ar"},
{"topicId":1,"subTopicId":2,"topicName":"J","subTopicName":" Us"},
{"topicId":1,"subTopicId":3,"topicName":"J","subTopicName":" Ut"},
{"topicId":2,"subTopicId":4,"topicName":"L","subTopicName":" Ov"},
{"topicId":2,"subTopicId":5,"topicName":"L","subTopicName":" El"},
{"topicId":2,"subTopicId":6,"topicName":"L","subTopicName":" In"},
{"topicId":2,"subTopicId":7,"topicName":"L","subTopicName":" Pr"},
{"topicId":2,"subTopicId":8,"topicName":"L","subTopicName":" Va"},
{"topicId":2,"subTopicId":9,"topicName":"L","subTopicName":" Pa"}
]
I have code that I use to reformat this data and just give me topic information:
var topics = data.map(function (t) {
return {
id: t.topicId, name: t.topicName
};
});
But this gives me three entries for topicId 1 and six entries for topidId 2.
How I can filter out duplicate entries so I can for example the above would just give me a topic array of two entries. One for each topicId
Please no jQuery, lodash or other framework solutions as I didn't include these in the tags. thanks
This should work
topics = data.filter(function(item, index, data) {
for (var i = 0; i < data.length; i++) {
if (item.topicId === data[i].topicId) break;
}
return index === i;
}).map(function (item) {
return {
id: item.topicId,
name: item.topicName
};
});
If duplicate entries are equal, you can simplify filter function
data.filter(function(item, index, data) {
return data.indexOf(item) === index;
})
Here is the solution:
var data =
[
{"topicId":1,"subTopicId":1,"topicName":"J","subTopicName":" Ar"},
{"topicId":1,"subTopicId":2,"topicName":"J","subTopicName":" Us"},
{"topicId":1,"subTopicId":3,"topicName":"J","subTopicName":" Ut"},
{"topicId":2,"subTopicId":4,"topicName":"L","subTopicName":" Ov"},
{"topicId":2,"subTopicId":5,"topicName":"L","subTopicName":" El"},
{"topicId":2,"subTopicId":6,"topicName":"L","subTopicName":" In"},
{"topicId":2,"subTopicId":7,"topicName":"L","subTopicName":" Pr"},
{"topicId":2,"subTopicId":8,"topicName":"L","subTopicName":" Va"},
{"topicId":2,"subTopicId":9,"topicName":"L","subTopicName":" Pa"}
]
var arr = [],
collection = [];
$.each(data, function (index, value) {
if ($.inArray(value.topicId, arr) == -1) {
arr.push(value.topicId);
collection.push(value);
}
});
console.log(collection);
This will print the following into console:
Try this
var topicIds = {};
var unique = [];
topics.forEach(function(t){
if(!topicIds[t.id]){
unique.push(t);
topicIds[t.id] = true;
}
});
unique will have unique array of topics.
I suggest to use the smart library underscore.js http://underscorejs.org/
They implement functional behaviors like groupBy http://underscorejs.org/#groupBy
So you can simply use _.groupBy(data, function(t){return t.topicId;}) and you get grouped suptopics:
Object {1: Array[3], 2: Array[6]}
1: Array[3]
[{
subTopicId: 1
subTopicName: " Ar"
topicId: 1
topicName: "J"
},{
subTopicId: 2
subTopicName: " Us"
topicId: 1
topicName: "J"
}, ... ]

In Javascript, how can I Rename/Renumber a set of properties?

This is one of those questions I'm ashamed to even ask, but I'm working with an external JSON source and I'm forced to do something ugly. So here goes...
I have 'dirty' Javascript object, with property names containing a number at their end:
{ "Friend1" : "Bob",
"Friend6" : "Fred",
"Friend632" : "Gonzo",
"FriendFinder1" : "Dolly",
"FriendFinder4294" : "Jan"
}
I'm trying to figure out a way to clean-up/"zero-index" these property names so the object would look like:
{ "Friend0" : "Bob",
"Friend1" : "Fred",
"Friend2" : "Gonzo",
"FriendFinder0" : "Dolly",
"FriendFinder1" : "Jan"
}
I'm referencing this indexOf/Regex code:
Is there a version of JavaScript's String.indexOf() that allows for regular expressions?
Any strategies you could recommend for doing this? I'll post where I'm at in a bit. Many thanks!
Take the "base" of a key and append items with a common base to an array using the original index. (This produces a sparse array.) Then stretch it out again by enumerating each item with a common base into a new key with 'base'+enumeratedindex.
The trick here is to use a method like forEach to enumerate the array--this will only visit assigned items in a sparse array, allowing you to determine the sort order just by using the original index-part of the key.
If you don't have access to forEach, you can accomplish a similar task by including the key in the array items. Instead of an intermediate array like this:
{Friend: [undefined, "Bob", undefined, undefined, undefined, undefined, "Fred"]}
You have one like this:
{Friend: [[6, 'Fred'],[1, 'Bob']]}
Then you sort the array and visit each item in a foreach loop, extracting the second item.
Here is code:
function rekey(obj) {
var rekey = /^(.*?)(\d+)$/;
var nestedobj = {}, newobj = {};
var key, basekeyrv, newkey, oldidx, newidx;
function basekey(key) {
return rekey.exec(key).splice(1);
}
for (key in obj) {
if (obj.hasOwnProperty(key)) {
basekeyrv = basekey(key);
newkey = basekeyrv[0];
oldidx = parseInt(basekeyrv[1], 10);
if (!nestedobj[newkey]) {
nestedobj[newkey] = [];
}
nestedobj[newkey][oldidx] = obj[key];
}
}
for (key in nestedobj) {
if (nestedobj.hasOwnProperty(key)) {
newidx = 0;
nestedobj[key].forEach(function(item){
newobj[key+newidx++] = item;
});
}
}
return newobj;
}
rekey({
"Friend1" : "Bob",
"Friend6" : "Fred",
"Friend632" : "Gonzo",
"FriendFinder1" : "Dolly",
"FriendFinder4294" : "Jan"
});
produces
{Friend0: "Bob",
Friend1: "Fred",
Friend2: "Gonzo",
FriendFinder0: "Dolly",
FriendFinder1: "Jan"}
Alternatively, without using forEach:
function rekey(obj) {
var rekey = /^(.*?)(\d+)$/;
var nestedobj = {}, newobj = {};
var key, basekeyrv, newkey, oldidx, newidx;
function basekey(key) {
return rekey.exec(key).splice(1);
}
for (key in obj) {
if (obj.hasOwnProperty(key)) {
basekeyrv = basekey(key);
newkey = basekeyrv[0];
oldidx = parseInt(basekeyrv[1], 10);
if (!nestedobj[newkey]) {
nestedobj[newkey] = [];
}
nestedobj[newkey].push([oldidx, obj[key]]);
}
}
for (key in nestedobj) {
if (nestedobj.hasOwnProperty(key)) {
nestedobj[key].sort();
for (newidx = 0; newidx < nestedobj[key].length; newidx++) {
newobj[key+newidx] = nestedobj[key][newidx][1];
}
}
}
return newobj;
}
Could you try doing the following:
{
friend: new Array(),
friendFinder: new Array()
}
then you can:
friend.push() - Add to array
var index = friend.indexOf("Bob") - find in array
friend.splice(index, 1) - remove from the array at index the 1 is for the number to remove.

In Javascript, how to merge array of objects?

var array1 = [{ "name" : "foo" , "age" : "22"}, { "name" : "bar" , "age" : "33"}];
var array2 = [{ "name" : "foo" , "age" : "22"}, { "name" : "buz" , "age" : "35"}];
What's the fastest way to have (no duplicates, name is the identifier):
[{ "name" : "foo" , "age" : "22"}, { "name" : "bar" , "age" : "33"}, { "name" : "buz" , "age" : "35"}];
With and without jquery if possible.
Here's a general purpose function that would merge an arbitrary number of arrays, preventing duplicates of the passed in key.
As it is merging, it creates a temporary index of the names used so far and only merges new elements that have a unique name. This temporary index should be much faster than a linear search through the results, particularly as the arrays get large. As a feature this scheme, it filters all duplicates, even duplicates that might be in one of the source arrays.
If an element does not have the keyName, it is skipped (though that logic could be reversed if you want depending upon what error handling you want for that):
var array1 = [{ "name" : "foo" , "age" : "22"}, { "name" : "bar" , "age" : "33"}];
var array2 = [{ "name" : "foo" , "age" : "22"}, { "name" : "buz" , "age" : "35"}];
function mergeArrays(keyName /* pass arrays as additional arguments */) {
var index = {}, i, len, merge = [], arr, name;
for (var j = 1; j < arguments.length; j++) {
arr = arguments[j];
for (i = 0, len = arr.length; i < len; i++) {
name = arr[i][keyName];
if ((typeof name != "undefined") && !(name in index)) {
index[name] = true;
merge.push(arr[i]);
}
}
}
return(merge);
}
var merged = mergeArrays("name", array1, array2);
// Returns:
// [{"name":"foo","age":"22"},{"name":"bar","age":"33"},{"name":"buz","age":"35"}]
You can see it work here: http://jsfiddle.net/jfriend00/8WfFW/
When this algorithm is run against the Matt algorithm in jsperf using larger arrays, this algorithm is around 20x faster:
What you have are completely different objects, and there is nothing built into JavaScript to detect identical objects; i.e. objects which have the same attributes, so we have to write our own function:
function merge(set1, set2) {
// Already put the elements of set1 in the result array
// see Array.slice
var result = set1.slice(0);
// Utility function which iterates over the elements in result
// and returns true if an element with the same name already
// exists. false otherwise
function exists(obj) {
for (var i=0;i<result.length;i++) {
if (result[i].name == obj.name) {
return true;
}
}
return false;
}
// Now simply iterate over the second set and add elements
// which aren't there already using our exists() function.
for (var i=0;i<set2.length;i++) {
if (!exists(set2[i])) {
result.push(set2[i]);
}
}
return result;
}
You'd then call it with;
var result = merge(array1, array2);
To become more confident with object equality try the following experiment;
var a = { "test": 1 };
var b = { "test": 1 };
var aClone = a;
alert(a == a); // true
alert(a == b); // false
alert(a == aClone); // true
I don't think that plain javascript offers anything better than iterating the array's and manually implementing the logic for that. What I would advice is to use the awesome underscore.js library that provides many functional-like facilities to handle arrays and collections in general; to solve your problem for example this could work:
http://documentcloud.github.com/underscore/#union
jQuery is another option, but it is more a DOM-manipulation browser-oriented library, while underscore is made to deal with these kind of problems.
The first way I can think of:
array3 = [];
for(object in array1) {
var match=false;
for(already in array3) {
if (already==object) {
match=true;
break; } }
if (match) array3.push(object); }

Categories

Resources