Related
How do I set deep properties in a JavaScript object using a dot-syntax string to specify which property I want to change?
For simple objects, I could just use data['property_name'] = 'foo', but I don't necessarily know how deeply nested the data is going to be.
Below is some example code with how I'd like to be able to format the data in the end. For all I know there's a nice way that JS already allows you to do this, but I haven't been able to find it yet.
Plunker here.
var items = [
{
lookup_string: "User.UserProfile.name",
value: "John Smith"
},
{
lookup_string: "User.email",
value: "johnsmith#example.com"
},
]
var data = {};
items.forEach(function(item){
// Inside this loop, set the appropriate keys under data. Is there a non-convoluted way to do this?
});
console.log("items", items);
console.log("Results", data)
// In the end, data should look like this:
var desiredData = {
User: {
UserProfile: {
name: 'John Smith'
},
email: 'johnsmith#example.com'
}
}
You could split the lookup_string and reduce an object with a default object. Later assign the value.
function setValue(object, path, value) {
var keys = path.split('.'),
last = keys.pop();
keys.reduce(function (o, k) {
return o[k] = o[k] || {};
}, object)[last] = value;
}
var items = [{ lookup_string: "User.UserProfile.name", value: "John Smith" }, { lookup_string: "User.email", value: "johnsmith#example.com" }],
object = {};
items.forEach(function(o) {
setValue(object, o.lookup_string, o.value);
});
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }
you will have to loop over all the keys from lookup_string.split('.') and assign values, something like this:
var items = [
{
lookup_string: "User.UserProfile.name",
value: "John Smith"
},
{
lookup_string: "User.email",
value: "johnsmith#example.com"
},
];
var data = {};
items.forEach(function(item){
var lookup = item.lookup_string.split('.');
var lastKey;
lookup.map(function(key){
data[key] = {};
lastKey = key;
});
data[lastKey] = item.value;
});
console.log("items", items);
console.log("Results", data)
// In the end, data should look like this:
var desiredData = {
User: {
UserProfile: {
name: 'John Smith'
},
email: 'johnsmith#example.com'
}
};
I thought my map and pluck functions are collection correct, but when I try to call pluck to get "type" of car object, it returns an empty array....? empty array...???
update: thanks for pointing out that it is object.....So I added for in in my map function, but seems not working? is anything wrong?
function map(collection,iterator){
var result=[];
if(Array.isArray(collection)){
for (var i=0;i<collection.length;i++){
result.push(iterator(collection[i]));
}
}else{
for (var key in collection){
result.push(iterator(collection[key]))};
}
return result;
};
function pluck(collection, key) {
return map(collection, function (value) {
return value[key]
});
}
var car = { type: "Fiat", model: "500", color: "white" };
console.log(pluck(car, function (auto) {
return auto.type;
}));
> the result: []
console.log(pluck(car, function (auto) {
return auto.type;
}));
should be
console.log(pluck(car, 'type'));
Also, car needs to be an array.
var car = [{ type: "Fiat", model: "500", color: "white" }];
You are passing this
var car = { type: "Fiat", model: "500", color: "white" };
While your map function expects array.
Use for-in loop if you want to pass object.
Pluck function only makes sense for arrays, because only arrays make sense to map. For objects you want to just read property:
var car = { type: "Fiat", model: "500", color: "white" };
console.log( car.type );
function map(collection, filter) {
var result = [];
for (var i = 0; i < collection.length; ++i) {
result.push(filter(collection[i]))
}
return result;
}
function pluck(collection, filter) {
return map(collection, filter);
}
var cars = [{type: "Fiat", model: "500", color: "white"}]
var result = pluck(cars, function(auto) {
return auto.type;
})
document.write(JSON.stringify(result))
because its an object, not array
I need to make an extension to existing code, can't change it.
There's this array:
var availableTags = [
{ label: "Yoga classes", category: "EDUCATIONAL" },
{ label: "Cooking classes", category: "EDUCATIONAL" },
{ label: "Cheese tastings", category: "EDUCATIONAL" },
{ label: "Maker Workshops", category: "PRACTICAL" },
{ label: "Seminars", category: "PRACTICAL" },
//many more of these
];
Now I need to check if a text entered in an input box is included in one of the labels, e.g. if the user enters "Yoga classes" => OK, if "Yoga" => NOK, "sdsdf" => NOK, etc.
What is the best way to do this? I am not sure I can use Array.indexOf as I am not sure how to pass the Object to the function, I would try looping through the array (around 40 entries) and compare each object.
You need to loop over every item in availableTags and check whether that item's label is equal to some input. Try something like this:
var input = "Yoga classes";
var found = false;
for (var i = 0, j = availableTags.length; i < j; i++) {
var cur = availableTags[i];
if (cur.label === input) {
found = true;
break;
}
}
console.log(found);
DEMO: http://jsfiddle.net/k4cp4/4/
Where this can easily be put into a function, like:
var checkMatch = (function () {
var availableTags = [
{ label: "Yoga classes", category: "EDUCATIONAL" },
{ label: "Cooking classes", category: "EDUCATIONAL" },
{ label: "Cheese tastings", category: "EDUCATIONAL" },
{ label: "Maker Workshops", category: "PRACTICAL" },
{ label: "Seminars", category: "PRACTICAL" }
];
return function (input) {
var found = false;
for (var i = 0, j = availableTags.length; i < j; i++) {
var cur = availableTags[i];
if (cur.label === input) {
found = true;
break;
}
}
return found;
};
})();
DEMO: http://jsfiddle.net/k4cp4/5/
This checks for an exact match. So if you want a case insensitive match, you can use:
if (cur.label.toLowerCase() === input.toLowerCase()) {
DEMO: http://jsfiddle.net/k4cp4/6/
If you want to see if any of the labels contain the input, you can use indexOf like:
if (cur.label.indexOf(input) > -1) {
DEMO: http://jsfiddle.net/k4cp4/7/
You can use Array.some method:
Tests whether some element in the array passes the test implemented by the provided function.
Then your code would look something like:
var isFound = availableTags.some(function(el) {
return el.label === 'Yoga classes';
});
Note: some method needs to be shimmed.
var check = function(item) {
for(at in availableTags) {
if(item == availableTags[at].label) {
return true;
}
}
return false;
}
console.log(check("Yoga classes"));
(context)I have information from a bunch of elements that I'm collecting into a JSON object that then gets passed down to an MVC3 controller where it gets deserialized into an object.
There are 'items' and 'item settings'. Currently, I have have both items and item settings all in flat JSON object. Ideally I would like to have the item settings nested under each item. My code currently looks like this:
var editeditems=[];
...
$("#SaveChanges").click(function() {
//this works and retrieves all of the item IDs
$(".portlet").each(function() {
var itemname = $(this).data("itemname");
editeditems.push(
{
"itemname": itemname
});
itemname = $(this).data("itemname");
$(".settingInput").each(function() {
editeditems.push(
{
"settingkey":$(this).attr("name"),
"settingvalue":$(this).attr("value")
});
});
});
Under the $(".settingInput").each function is where the settings get added. I've tried syntax like 'editedItems.settings.push..' but it returns with a syntax error.
Any help would greatly be appreciated!
var editeditems = [];
...
$('#SaveChanges').click(function() {
$('.portlet').each(function() {
var settings = [];
$('.settingInput').each(function() {
settings.push({
settingkey: $(this).attr('name'),
settingvalue: $(this).attr('value')
});
});
editeditems.push({
itemname: $(this).data('itemname'),
settings: settings
});
});
...
});
will generate sample output:
var editeditems =
[
{
"itemname": "item1",
"settings": [
{
"settingkey": "key1",
"settingvalue": "value1"
},
{
"settingkey": "key2",
"settingvalue": "value2"
}
]
},
{
"itemname": "item2",
"settings": [
{
"settingkey": "key1",
"settingvalue": "value3"
},
{
"settingkey": "key2",
"settingvalue": "value4"
}
]
}
];
var ei = {'settings': [3]};
ei.settings.push(4);
console.log(ei);
// This will output an object with property settings and value an array with values (3 and 4)
You need to create flat data array json as:
[{"itemname": "item1","settingkey": "key1","settingvalue": "value1"},
{"itemname": "item2","settingkey": "key2","settingvalue": "value2"},];
Than process the above date like this
var keys = Object.keys(dataMap);
var json = [];
for (var key in keys) {
var innerJson = {};
innerJson["name"] = keys[key];
var innerMap = dataMap[keys[key]];
if (innerMap instanceof Array) {
innerJson["size"] = innerMap[0];
} else if (innerMap instanceof Object) {
var child = processHirarchiachalData(innerMap);
innerJson["children"] = child;
}
json.push(innerJson);
}
pretty simple question, can't quite fig. it out.
I have 2 js array's that I need to combine into a new array, based on sub_key.
var items = [
Object {
OBJECTID=1,
Name="COMMAND B",
ID="AR0xx",
sub_key="1000"
},
Object {
OBJECTID=2,
Name="95TH PCT",
ID="AR0xx",
sub_key="1001"
},
Object {
OBJECTID=379,
Name="dummy4",
ID="AR0xx",
sub_key="9999"
}
];
var subitems = [
Object {
OBJECTID=787,
ID="AR0xx",
sub_key=1000,
Long_Name = foo
},
Object {
OBJECTID=789,
ID="AR0xx",
sub_key=1001,
Long_Name = "bar"
},
Object {
OBJECTID=1,
ID="AR0xx",
sub_key=1001,
Long_Name="baz"
},
Object {
OBJECTID=788,
ID="AR0xx",
sub_key=1001,
Long_Name="buzzz"
}
];
I'd like to create an array like so, which just combines the above 2, based on sub_key
var data = [
COMMAND B=["foo"],
95TH PCT=["bar","baz","buzz"]
dummy4=[]
];
Here's what I tried but it doesn't work... i think i'm close?? thanks for any help!
data = [];
for (var key in items){
var o = items[key];
//data.push(o.Name);
for (var subkey in subitems){
subo = subitems[subkey];
if (o.sub_key == subo.sub_key){
data[o.Name].push(subo.Long_Name)
}
}
}
Cleaning up your script, here is what you are trying to do. It craetes an array of objects using the Name from items and matching sub_key from sub_items.
var items = [
{ OBJECTID: 1,
Name: 'COMMAND B',
ID: 'AR0xx',
sub_key: '1000'
},
{ OBJECTID: 2,
Name: '95TH PCT',
ID: 'AR0xx',
sub_key: '1001'
},
{ OBJECTID: 379,
Name: 'dummy4',
ID: 'AR0xx',
sub_key: '9999'
}
];
var subitems = [
{ BJECTID: 787,
ID: 'AR0xx',
sub_key: '1000',
Long_Name: 'foo'
},
{ OBJECTID: '789',
ID: 'AR0xx',
sub_key: '1001',
Long_Name: 'bar'
},
{ OBJECTID: '1',
ID: 'AR0xx',
sub_key: 1001,
Long_Name: 'baz'
},
{ OBJECTID: '788',
ID: 'AR0xx',
sub_key: '1001',
Long_Name: 'buzzz'
}
];
var j = subitems.length;
var result = {};
var p;
var sub_key;
var obj;
for (var i=0, iLen = items.length; i<iLen; i++) {
p = items[i].Name;
result[p] = [];
sub_key = items[i].sub_key;
for (var j=0, jLen=subitems.length; j<jLen; j++) {
if (subitems[j].sub_key == sub_key) {
result[p].push(subitems[j].Long_Name);
}
}
}
alert(result['95TH PCT']); // bar, baz, buzz
Edit
Return a single object rather than an array of objects, which I think is what is required.
var newarray = items.slice(0); // make a copy
addloop: for (var i=0; i<subitems.length; i++) {
for (var j=0; j<newarray.length; j++)
if (subitems[i].sub_key == newarray[j].sub_key)
continue addloop;
newarray.push(subitems[i]);
}
should work. Another solution:
Array.prototype.combine = function(a, test) {
if (typeof test == "function") {
for (var i=0; i<a.length; i++)
if (! this.some(test.bind(null, a[i])))
this.push(a[i]);
} else {
for (var i=0; i<a.length; i++)
if (this.indexOf(a[i]) == -1)
this.push(a[i]);
}
return this;
};
var newarray = items.slice(0).combine(subitems, function(a, b) {
return a.sub_key == b.sub_key;
});
I wrote this on the train but didn't get to post it, and it looks like a couple of other people posted good answers since, but it might still be helpful so I'll post it anyways
I had a few different things to note:
var items = [
Object {
OBJECTID=1,
Name="COMMAND B",
ID="AR0xx",
sub_key="1000"
},
...
You don't need the word Object here, you can just write { ... } and JS knows it's an object.
Within an object, you need : instead of =
It's not required, but putting the key in quotes is good practice because some keys won't work otherwise.
So it should look like this:
var items = [
{
"OBJECTID": 1,
"Name": "COMMAND B",
"ID": "AR0xx",
"sub_key": "1000"
},
...
Next up, I'm not completely clear on what you're doing with your data array in the second block, but it looks like you're overriding it with an empty array in the third block.
Also, I think you may be confusing Objects and Arrays somewhat. http://nfriedly.com/techblog/2009/06/advanced-javascript-objects-arrays-and-array-like-objects/ has a good overview of the differences, but here's some key points:
Array is a subclass of Object
Array values always have numeric indexes, not string keys
push() is a method of Array not Object
Next up, your loop. for .. in style loops do work on arrays, but they're not generally recommended because they can also hit keys that were added to the underlying Object. forEach is my favorite but it's not always available in older browsers without a library such as underscore.js.
for(var i=0, len=MyArray.length; i<len; i++) {...} is the other option that you'll see very commonly because it covers all of the array items but does not have the possibility of hitting the underlying object.
But, since Bergi and RobG both have good loops, I'll stop here.