I have a JSON string that is similar to below:
[
{"id":"112233","region":"UK","city":"London","name":"Company 1"},
{"id":"112244","region":"UK","city":"London","name":"Company 2"},
{"id":"112255","region":"UK","city":"Manchester","name":"Company 3"},
{"id":"112266","region":"UK","city":"Manchester","name":"Company 4"}
]
I am trying to rebuild this into a JS array like this:
[
{
["London"]: [
["112233"] : [{"id":"112233","region":"UK","city":"London","name":"Company 1"}],
["11224"] : [{"id":"112244","region":"UK","city":"London","name":"Company 2"}],
],
["Manchester"]: [
["112255"] : [{"id":"112255","region":"UK","city":"Manchester","name":"Company 3"}],
["112266"] : [{"id":"112266","region":"UK","city":"Manchester","name":"Company 4"}]
]
}
]
Here is the code I am using to do this:
var company = [];
var companies = [];
var cities = [];
// generate citites
for (var i = 0; i < dump.length; i++)
{
// check if city exits
if(!cities.includes(dump[i].city.trim())) {
cities[dump[i].city.trim()] = companies;
}
}
// add companies
for (var i = 0; i < dump.length; i++)
{
company['company_name'] = dump[i].company_name;
company['region'] = dump[i].region;
cities[dump[i].city][dump[i].id] = company;
}
console.log(cities);
Now I get an error stating Cannot set property '112233' of undefined TypeError: Cannot set property '112233' of undefined.
Can someone explain what I'm doing wrong?
The formatting of your desired results is a little strange because you are using [] for what looks like objects with keys. I'm assuming that's a typos and that you really want an object.
Here's a quick easy way to do that with reduce():
let dump = [
{"id":"112233","region":"UK","city":"London","name":"Company 1"},
{"id":"112244","region":"UK","city":"London","name":"Company 2"},
{"id":"112255","region":"UK","city":"Manchester","name":"Company 3"},
{"id":"112266","region":"UK","city":"Manchester","name":"Company 4"}
]
let obj = dump.reduce((obj, item) => {
let city = obj[item.city] || (obj[item.city] = {}) // add city obj to object if not there.
city[item.id] = item // add item.id to city obj
return obj
}, {})
console.log(obj)
EDIT:
The way reduce() works is to start with a value that is passed in the second parameter, here that's an empty object {} that is called obj in the callback, and then iterate through the array (dump). With each iteration we look and see if this obj has a property with the name of the current item in the iteration. If not add it and assign a new object {}. Then with that object in hand, add a property corresponding to item.id and adding the whole item to it.
You could write the entire thing as a for loop, but reduce is pretty succinct — it just takes a while to get used to it.
Related
I have one object. The first element inside the data object looks like this:
data[0] = {name:"Bob", model:"Tesla", color:"white"};
and a second object, whose first element looks like this:
new_data[0] = {salary:"50000", age:"34"};
data and new_data are the same length, and each element inside of the new_data object needs to be appended onto the correlating data object, to make something like this:
data[0] = {name:"Bob", model:"Tesla", color:"white", salary:"50000", age:"34"};
I've used concat before to add elements into a single line object ( var
people = ["Dan","Bob"];
people.concat("Mike");
, but that same idea doesn't work here:
for ( var i = 0;i<data.length; i++ ) {
data[i] = data[i].concat(new_data[i]);
}
How do I go about looping through this?
As such you have tagged your question with jQuery, I've used its $.extend() method below (jQuery Documentation).
This is just a one liner solution for your case. By passing true to this method, you can easily merge object2 into object1, recursively. jQuery is smart to figure out that both of your objects are array of same length, so the output is an array and each item in the resulting array is a merged result from both objects.
Object.assign is quite new (ES6) and may not be supported in all browsers (Source). But this jQuery way can be a useful time saver for supporting all the browsers.
var collection1 = [{
name: "Bob",
model: "Tesla",
color: "white"
},
{
name: "Bob 1",
model: "Tesla 1",
color: "white 1"
}
];
var collection2 = [{
salary: "50000",
age: "34"
},
{
salary: "50001",
age: "35"
}
];
var result = $.extend(true, collection1, collection2);
console.log(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
MDN
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.
How do I go about looping through this?
With Object.assign() in mind we can loop through it like below:
var data = [];
data[0] = {name:"Bob", model:"Tesla", color:"white"};
data[1] = {name:"Martin", model:"Ford", color:"Blue"};
data[2] = {name:"Danny", model:"BMW", color:"Purple"};
var new_data =[];
new_data[0] = {salary:"50000", age:"34"};
new_data[1] = {salary:"45000", age:"24"};
new_data[2] = {salary:"10000", age:"39"};
for(var i = 0; i < data.length; i++){
data[i] = Object.assign(data[i], new_data[i]);
console.log(data[i]);
}
You can use jQuery.extend function like shown below. It extends existing data[i] object with properties from new_data[i] object.
for ( var i = 0; i<data.length; i++ ) {
jQuery.extend(data[i], new_data[i]);
}
The most efficient way is to loop through every element in new_data and apply it to data.
for(var key in new_data[0]){
data[0][key] = new_data[0][key];
}
What you need to use if Object.assign like so :
for ( var i = 0;i < data.length; i++ ) {
Object.assign(data[i], new_data[i]);
}
This will alter the content of data.
concat is meant to be used with arrays. It will append one (or more) array(s) at the end of another.
let people = ["Dan","Bob"];
people.concat(["Mike"]); // people is now ["Dan", "Bob", "Mike"]
You could utilize the keys of one of the object to merge the two. This would work in IE9 and up as well. If you need to guard against overriding a property in your base object then you could add a quick truthy check for that key before assigning in the forEach invocation.
var obj1 = {id:1, name: "bob"}
var obj2 = {dob: "2000101"};
Object
.keys(obj1)
.forEach(function(k){
obj2[k] = obj1[k];
});
console.log(obj2);
I have an array of objects that looks like the image below. Is there a way by which I can have an array that contains unique objects with respect to id ? We can see below that the id are same at index [0] and index [2].
Is there a way that I can get an array containing objects with unique id and the first object from the last index is added to the unique array rather than the first object. In this case, Object at index[2] should be added instead of object at index[0]:
To get an array of "unique" objects(with last index within the list) for your particular case use the following approach (Array.forEach, Array.map and Object.keys functions):
// exemplary array of objects (id 'WAew111' occurs twice)
var arr = [{id: 'WAew111', text: "first"}, {id: 'WAew222', text: "b"}, {id: 'WAew111', text: "last"}, {id: 'WAew33', text: "c"}],
obj = {}, new_arr = [];
// in the end the last unique object will be considered
arr.forEach(function(v){
obj[v['id']] = v;
});
new_arr = Object.keys(obj).map(function(id) { return obj[id]; });
console.log(JSON.stringify(new_arr, 0, 4));
The output:
[
{
"id": "WAew111",
"text": "last"
},
{
"id": "WAew222",
"text": "b"
},
{
"id": "WAew33",
"text": "c"
}
]
The best way to do this is to modify your data structure into an object itself where each key is one of the IDs:
{
"WadWA7WA6WAaWAdWA...": {
"text": "birla"
},
"WadWA...": {
"test": "ab"
}
}
and so forth. If the data comes from a source formatted that way, you can always map the array of results to this format.
You could create a hash using the id as the key and keeping the value as the entire object:
var myHash = new Object();
var i;
for(i = 0; i < yourArray.length; i++) {
var yourObjId = yourArray[i][id];
myHash[yourObjId] = yourArray[i];
}
You would be left with a hash myHash containing objects with unique id's (and only the last object of duplicates would be stored)
Try this: just add to a new object using id as the key
var arr = [{id:'123', text: 'a'}, {id:'234', text: 'b'}, {id:'123', text: 'c'}];
var map = new Object();
for(var i in arr){ map[arr[i].id] = arr[i]; }
var newArr = [];
for(var i in map){ newArr.push(map[i]); }
newArr shall contain the 2nd and 3rd object.
Lets say I have this object here:
var items = [
{name:"Foo"},
{name:"Bar"},
{name:"foo"},
{name:"bar"},
{name:"foobar"},
{name:"barfoo"}
];
Since it only has one item in each object, I want to just return a list of them.
I tried this:
var getSingle = function(rows){
var items = [];
//This should only return one column
for (var i = 0; i < rows.length; i++) {
var r = rows[i];
var c = 0;
for (var n in r) {
if(c == 0)
items.push(r[n]);
c += 1;
}
}
return items;
}
But it doesn't seem to work. Any thoughts?
PS. name could be anything.
I used a different approach than the others, because I make two assumptions:
1/ you do not know the name of the key, but there is only one key for every item
2/ the key can be different on every item
I will give you a second option, with the second assumption as: 2/ all item have only one key but that's the same for all of them
First Options :
var items = [
{name:"Foo"},
{name:"Bar"},
{name:"foo"},
{name:"bar"},
{name:"foobar"},
{name:"barfoo"}
];
// object keys very simple shim
Object.keys = Object.keys || function(o) {
var result = [];
for(var name in o) {
if (o.hasOwnProperty(name))
result.push(name);
}
return result;
};
// function to get the value of every first keys in an object
// just remember that saying "first key" does not make real sense
// but we begin with the assumption that there IS ONLY ONE KEY FOR EVERY ITEM
// and this key is unknown
function getFirstKeysValues(items) {
var i = 0, len = items.length, item = null, key = null, res = [];
for(i = 0; i < len; i++) {
item = items[i];
key = Object.keys(item).shift();
res.push(item[key]);
}
return res;
}
console.log(getFirstKeysValues(items)); //["Foo", "Bar", "foo", "bar", "foobar", "barfoo"]
Second options will use a map, because we believe that every child possess the same key (I wouldn't use this one, because I do not like .map that much - compatibility):
var items = [
{name:"Foo"},
{name:"Bar"},
{name:"foo"},
{name:"bar"},
{name:"foobar"},
{name:"barfoo"}
];
// object keys very simple shim
Object.keys = Object.keys || function(o) {
var result = [];
for(var name in o) {
if (o.hasOwnProperty(name))
result.push(name);
}
return result;
};
// function to get the value of every first keys in an object
// just remember that saying "first key" does not make real sense
// but we begin with the asumption that there IS ONLY ONE KEY FOR EVERY ITEM
// and this key is unknown but the same for every child
function getFirstKeysValues(items) {
var key = items.length > 0 ? Object.keys(items[0]).shift() : null;
items = items.map(function (item) {
return item[key];
});
return items;
}
console.log(getFirstKeysValues(items));
This is usually accomplished using the map method, see the documentation here.
var justNamesArray = items.map(function(elem) { return elem.name});
The documenation page also includes a useful shim, that is a way to include it in your code to support older browsers.
Accompanying your request in the edit, if you would just like to get those that contain this property there is a nifty filter method.
var valuesWithNamePropert= items.filter(function(elem) { return elem.hasOwnProperty("name")});
You can chain the two to get
var justNamesWhereContains = items.filter(function(elem) { return elem.hasOwnProperty("name")}).
.map(function(elem) { return elem.name});
This approach (mapping and filtering), is very common in languages that support first order functions like JavaScript.
Some libraries such as underscore.js also offer a method that does this directly, for example in underscore that method is called pluck.
EDIT: after you specific that the property can change between objects in the array you can use something like:
var justReducedArray = items.map(function(elem) { for(i in elem){ return elem[i]}});
your var items = [] is shadowing your items parameter which already contains data. Just by seeing your code I thought that maybe your parameter should be called rows
If you're in a world >= IE9, Object.keys() will do the trick. It's not terribly useful for the Array of Objects, but it will help for the iteration of the Array (you would use Array.forEach to iterate the array proper, but then you would use the Object.keys(ob)[0] approach to get the value of the first property on the object. For example:
var someArr = [{ prop1: '1' },{ prop2: '2' },{ prop3: '3' }];
var vals = [];
someArr.forEach( function(obj) {
var firstKey = Object.keys(obj)[0];
vals.push(obj[firstKey]);
});
//vals now == ['1','2','3']
Obviously this isn't null safe, but it should get you an array of the values of the first property of each object in the original array. Say that 3 times fast. This also decouples any dependency on the name of the first property--if the name of the first property is important, then it's a trivial change to the forEach iteration.
You can override the Array.toString method for items, so using String(items) or alert(items) or items+='' will all return the string you want-
var items = [{name:"Foo"}, {name:"Bar"},{name:"foo"},
{name:"bar"},{name:"foobar"},{name:"barfoo"}];
items.toString= function(delim){
delim=delim || ', ';
return this.map(function(itm){
return itm.name;
}).join(delim);
}
String(items)
/* returned value: (String)
Foo, Bar, foo, bar, foobar, barfoo
*/
instead of the default string-'[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]'
if I have an array like this:
var msg = [ {name: ["a1", "a2"], value: "this is A"},
{name: ["b1", "b2"], value: "this is B"},
...
]
The array contains global errors messages for client-side form validations. I have managed to pass in faulty inputs (e.g. "a1") and now am wondering how to get the corresponding message out of my ill-constructed array.
Question
What would be the best way to loop through this array? for example, if I have "a1" as parameter passed into my function, how do I extract "this is A" as corresponding message?
inArray doesn't really help, because I need the corresponding message and not the position of a1. I'm also not sure if this is the best way to store my error messages... ideas welcome!
Thanks for help!
Re-arrange your data structure:
var my_param = 'b1';
// This is an object, so we can have key/value pairs
var error_codes =
{
'a1': 0,
'a2': 0,
'b1': 1,
'b2': 1
};
// This is an array because we only need values
var error_messages =
[
'This is A',
'This is b'
];
alert(error_messages[error_codes[my_param]]);
This makes it really easy to set up new error codes and messages, and is extremely easy to understand. The only gotcha is error_codes[my_param] - it's an object, but we can't do error_codes.my_param because it'll look for the element called 'my_param', so using array notation, we can look up the object key.
The only other potential trap is making sure you don't have any trailing commas:
var error_codes = { 'a1': 1, }; // NO!
Also knows as the trailing comma of death!
This would be how I'd do it
var myMsg = findMsg('a1')
function findMsg(msgType){
msg.forEach(function(obj){
if(inArray(msgType, obj.name) !== -1){
return obj.value
}
})
}
function inArray(key, obj){
return obj.join().indexOf(key)
}
$.each is the jQuery way for taking action on each element of an array or each enumerable property of an object.
var value;
$.each(msg, function (i, el) {
if (el.name.indexOf(name) >= 0) {
value = el.value;
return false; // Stops iteration.
}
});
If name is "a1" then after running the above, value === "this is A".
Nice and simple:
var getMessage = function (name)
{
var msg = [ ... ];
for(var i = 0; i < msg.length; ++ i)
if (msg [i].name.indexOf (name) != -1)
return msg [i].value;
}
Returns either the corresponding message or undefined if the name wasn't found.
You may need a shim for indexOf depending on which browsers you want to support:
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf
My question is related to this question. You will have to first read it.
var ids = "1*2*3";
var Name ="John*Brain*Andy";
var Code ="A12*B22*B22";
Now that I have an array of javascript objects. I want to group my objects based on CODE. So there can be duplicate codes in that code string.
As per the above changed strings, I have same code for Brain and Andy. So, now I want two arrays. In one there will be only one object containing details of only John and in the other object there will be two objects containing details of Brain and Andy.
Just for example I've taken 3 items. In actual there can be many and also there can be many set of distinct codes.
UPDATE
I needed the structure like the one built in groupMap object by the #Pointy. But I will use #patrick's code to achieve that structure. Many thanks to both of them.
It is a little hard to tell the exact resulting structure that you want.
This code:
// Split values into arrays
Code = Code.split('*');
Name = Name.split('*');
ids = ids.split('*');
// cache the length of one and create the result object
var length = Code.length;
var result = {};
// Iterate over each array item
// If we come across a new code,
// add it to result with an empty array
for(var i = 0; i < length; i++) {
if(Code[i] in result == false) {
result[ Code[i] ] = [];
}
// Push a new object into the Code at "i" with the Name and ID at "i"
result[ Code[i] ].push({ name:Name[i], id:ids[i] });
}
Will produce this structure:
// Resulting object
{
// A12 has array with one object
A12: [ {id: "1", name: "John"} ],
// B22 has array with two objects
B22: [ {id: "2", name: "Brain"},
{id: "3", name: "Andy"}
]
}
Split the strings on "*" so that you have 3 arrays.
Build objects from like-indexed elements of each array.
While building those objects, collect a second object that contains arrays for each "Code" value.
Code:
function toGroups(ids, names, codes) {
ids = ids.split('*');
names = names.split('*');
codes = codes.split('*');
if (ids.length !== names.length || ids.length !== codes.length)
throw "Invalid strings";
var objects = [], groupMap = {};
for (var i = 0; i < ids.length; ++i) {
var o = { id: ids[i], name: names[i], code: code[i] };
objects.push(o);
if (groupMap[o.code]) {
groupMap[o.code].push(o);
else
groupMap[o.code] = [o];
}
return { objects: objects, groupMap: groupMap };
}
The "two arrays" you say you want will be in the "groupMap" property of the object returned by that function.