ng-options model bind based on property - javascript

Hi I am quite new to angular programming and I had a question on angular's ng-options.
I have an object structure like
TestObj: { name: 'Name1', subNames: ['Subname1'] }
TestArr:
[{ name: 'Name1', subNames: ['Subname1'] },
{ name: 'Name2', subNames: ['Subname1'] },
{ name: 'Name3', subNames: ['Subname1'] }]
And I am trying to use ng-options to bind one of the TestArr objects to TestObj.
<select class="touchpoint-settings-create-channel-box ng-model="TestObj" ng-options="curTest.name for curTest in TestArr"></select>
However, is there any way to make TestObj bind to the object in TestArr that has the same 'name' property?
This jsfiddle is a good example of what I mean. http://jsfiddle.net/KN9xx/840/
When it first starts up, selectedPerson is set to an object similar to the first item in the array, but ng-options does not realize it is the same object.
Thank you

I would not recommend following the example in that jsfiddle. Essentially, you do not want TestObj AND TestArr. If TestObj is not just a reference to TestArr, they are not going to be the same object and you're duplicating data because Angular and JavaScript do not recognize that the two objects have the same keys and values when they are separate objects.
You should follow the example in this SO post: How to have a default option in Angular.js select box
Rather than using TestObj like you are, use ng-init to set the default value.
var TestArr =
[{ name: 'Name1', subNames: ['Subname1'] },
{ name: 'Name2', subNames: ['Subname1'] },
{ name: 'Name3', subNames: ['Subname1'] }];
<select
class="touchpoint-settings-create-channel-box
ng-init="TestObj = options[0]"
ng-model="TestObj"
ng-options="option.name for option in TestArr">
</select>
To illustrate my point on objects:
var obj1 = { name: 'Me' };
var obj2 = { name: 'Me' };
While obj1 and obj2 look exactly the same, the variables are just references to the underlying object, not the values, and the JS interpreter doesn't know that I intend both of those objects to be the same. However, if you strings or numbers, it works like you expect.
var me = 'Me';
var stillMe = 'Me';
console.log(obj1 === obj2); // prints false
console.log(me === stillMe); // prints true

Related

Javascript: Spread array of an object in an object and change properties inside it only

I have a scenario where I want to change properties of object in an array. That array is wrapped inside another object.
const defaultData = {
title: "Title",
subtitle: "Subtitle",
books: [
{
bookId: "1",
imageSrc:
"any.png",
name: "Issue",
userOwnsData: true,
panelsCollected: 0,
totalPanels: 123,
link: "https://google.com",
},
],
bgColor: "black",
};
When I spread it like this:
{...defaultData, ...defaultData.books[0], panelsCollected:123} //previously it was 0
then it adds another extra object to parent object but not update it inside first index of books array
How can I just change that panelsCollected property without disturbing whole structure as we are using typescript.
Edit:
We can change it directly accessing the property too as we know index but that comes with a side effect of manipulating original dataset also which we should avoid and only copy needs to be updated.
Thanks
When spreading an object with nested properties with the intention of updating specific properties, think of it in two steps:
Spread the original object to copy it (...)
Redefine the new property values after the spread object
In your example we are doing the following:
Duplicating defaultData and assigning an updated books property (to be defined in the next step)
Duplicating the first book (defaultData.books[0]) and assigning an updated panelsCollected property to it. Then overwriting the existing books property with this updated array item
The result is as follows:
const defaultData = {
title: "Title",
subtitle: "Subtitle",
books: [
{
bookId: "1",
imageSrc:
"any.png",
name: "Issue",
userOwnsData: true,
panelsCollected: 0,
totalPanels: 123,
link: "https://google.com",
},
],
bgColor: "black",
};
const newBook = {
...defaultData,
books: [
{
...defaultData.books[0],
panelsCollected: 123
}
]
}
console.log(newBook)
/*
{
title: "Title",
subtitle: "Subtitle",
books: [
{
bookId: "1",
imageSrc:
"any.png",
name: "Issue",
userOwnsData: true,
panelsCollected: 123,
totalPanels: 123,
link: "https://google.com",
},
],
bgColor: "black",
};
*/
If for example the books property was 1000 items long, you would instead use have to find the specific book in your array using an array method (e.g. find / findIndex) and update it, e.g.
const bookToUpdateIndex = defaultData.books.findIndex(book => book.bookId === '1')
const updatedBooks = [...defaultData.books]
updatedBooks[bookToUpdateIndex] = {
...updatedBooks[bookToUpdateIndex],
panelsCollected: 123
}
const newBook = {
...defaultData,
books: updatedBooks
}
I think it is creating another parent object because you are using the spread twice. I tried to do 2 console logs. Please let me know if this is the result you are looking for.
console.log({...defaultData['books'][0]['panelsCollected'] = 10})
console.log(defaultData);
Instead of using find and the spread syntax an alternative approach (but not necessarily the most performant) might be to copy the object by stringifying it, and then reparsing that string. And then you can just update the object at the index you need.
const defaultData={title:"Title",subtitle:"Subtitle",books:[{bookId:"1",imageSrc:"any.png",name:"Issue",userOwnsData:!0,panelsCollected:0,totalPanels:123,link:"https://google.com"}],bgColor:"black"};
const copy = JSON.parse(JSON.stringify(defaultData));
copy.books[0].panelsCollected = 123;
console.log(defaultData);
console.log(copy);

How to change the location of an object key value pair in JavaScript

I've seen similar questions to this one but in different languages and I am struggling to create a JavaScript equivalent.
I am receiving an object and through a function I want to change the location of one (or more) of the properties. For example,
With the original object of
{
individual: [
{
dob: '2017-01-01',
isAuthorized: true,
},
],
business: [
{
taxId: '123',
},
],
product: {
code: '123',
},
}
I would like to change the location of isAuthorized to be in the first object inside of the business array instead of individual.
Like so
{
individual: [
{
dob: '2017-01-01',
},
],
business: [
{
taxId: '123',
isAuthorized: true,
},
],
product: {
code: '123',
},
}
So far I was trying to create an object that would contain the key name and location to change it to, e.g.
{
isAuthorized: obj.business[0]
}
And then loop over the original object as well as the object with the location values and then set the location of that key value pair.
Basically, in this function I want to see that if the original object contains a certain value (in this case isAuthorized) that it will take that key value pair and move it to the desired location.
What you want can easily be achieved by using loadsh, here's a working snippet of how to restructure based on defined structure map. Extended this example to match what you want.
The example is doing a deep clone, if you are fine modifying the original object then skip that step to avoid the overhead.
// input data
const data = {
individual: [
{
dob: '2017-01-01',
isAuthorized: true,
},
],
business: [
{
taxId: '123',
},
],
product: {
code: '123',
},
};
// the structure change map
const keyMap = {
'individual[0].isAuthorized': 'business[0].isAuthorized'
};
function parseData(data,keyMap) {
const newData = _.cloneDeep(data);
for( let [source,dest] of Object.entries(keyMap) ) {
_.set(newData,dest,_.get(newData,source));
_.unset(newData,source);
}
return newData;
}
console.log(parseData(data, keyMap));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
Note: loadsh's set consider any numeric value as an array index so if you are using a numeric object key then use loadash.setWith. I recommend reading examples in doc for a better understanding.
https://lodash.com/docs/4.17.15#set

json object from javascript nested array

I'm using a nested array with the following structure:
arr[0]["id"] = "example0";
arr[0]["name"] = "name0";
arr[1]["id"] = "example1";
arr[1]["name"] = "name1";
arr[2]["id"] = "example2";
arr[2]["name"] = "name2";
now I'm trying to get a nested Json Object from this array
arr{
{
id: example0,
name: name00,
},
{
id: example1,
name: name01,
},
{
id: example2,
name: name02,
}
}
I tought it would work with JSON.stringify(arr); but it doesen't :(
I would be really happy for a solution.
Thank you!
If you are starting out with an array that looks like this, where each subarray's first element is the id and the second element is the name:
const array = [["example0", "name00"], ["example1", "name01"], ["example2", "name02"]]
You first need to map it to an array of Objects.
const arrayOfObjects = array.map((el) => ({
id: el[0],
name: el[1]
}))
Then you can call JSON.stringify(arrayOfObjects) to get the JSON.
You need to make a valid array:
arr = [
{
id: 'example0',
name: 'name00',
},
{
id: 'example1',
name: 'name01',
},
{
id: 'example2',
name: 'name02',
}
];
console.log(JSON.stringify(arr));
Note that I am assigning the array to a variable here. Also, I use [] to create an array where your original code had {}.

Find a Value in a Dictionary with a certain Property

Say I have a dictionary like
objDict = {
id1 : { name: 'someObj1', id: 'someId1' },
id2 : { name: 'someObj2', id: 'someId2' },
id3 : { name: 'someObj3', id: 'someId3' },
}
If I wanted to search for the property "someId2" of the "id" property in Values of that dictionary.. how would I be able to grab the the whole object after finding it?
I really can't think of a good way to do it outside of iterating the dictionary with a for-in loop. I was thinking of using Object.values() but even then I can't think of a way of using that to grab the whole object that contains someId2.. I would only be able to determine if that property existed.
I was just wondering if there was a better way than a for-in loop. Thanks
you can just get all the keys using Object.keys(), then iterate over these and select the object you want based on the required id.
var objDict = {
id1 : { name: 'someObj1', id: 'someId1' },
id2 : { name: 'someObj2', id: 'someId2' },
id3 : { name: 'someObj3', id: 'someId3' },
};
var obj;
Object.keys(objDict).forEach(x => obj = objDict[x].id === 'someId2' ? objDict[x]: obj);
console.log(obj);
There is Object.entries(), but for-in loop is more efficient than the other alternatives.
objDict = { id1 : { name: 'someObj1', id: 'someId1' },
id2 : { name: 'someObj2', id: 'someId2' },
id3 : { name: 'someObj3', id: 'someId3' } }
item = Object.entries(objDict).find(a => a[1].id === 'someId2')
console.log(JSON.stringify(item))
The following code snippet containing break; statement could statistically provide a better performance vs. the selected one.
objDict = {
id1 : { name: 'someObj1', id: 'someId1' },
id2 : { name: 'someObj2', id: 'someId2' },
id3 : { name: 'someObj3', id: 'someId3' },
};
var obj;
for (var key in objDict)
{
if (objDict[key].id==='someId2')
{
obj = objDict [key];
break;
}
}
console.log(obj);
ECMA Script 2015 provides another alternative of using Map object as described in : How to iterate (keys, values) in javascript?
Hope this may help.
This is a generic indexing problem, so no, there's no good way to do this other than looping through each value in the dictionary. This is because you've created a data structure to access the data quickly (O(1) time), but the drawback is that if you want to find the data through another index quickly, you'll have to search manually O(n) time for it.
If you really did care about the actual value of someId, then the canonical answer would be to create 2 separate dictionaries w/ the same data w/ different key values. This is very similar to having 2 indexes on a database table.

JS – How to build a dynamic nested object of arrays with objects etc. from string

This is a nice evening project, but actually i'm stuck with some headache.
All I need is a function like this example:
result = set("itemCategories[0].items[0].name", "Test")
which should return:
{ itemCategories: [
{
items: [ {name: "Test"} ]
}
}]
...and in case of the given attribute "itemCategories[1].items[2].name" this result:
{ itemCategories: [
null,
{
items: [
null,
null,
{name: "Test"}
]
}
}]
Use lodash#set:
result = lodash.set({}, "itemCategories[0].items[0].name", "Test")
If you are asking about the vanilla JavaScript Set method then you could do this.
/* this is what you are trying to get.
{ itemCategories: [
{
items: [ {name: "Test"} ]
}
}]
*/
var mySet = new Set(); // your set object.
Create your data (number, text, string, object, array, null).
ver data1 = 365;
ver data2 = 'Dragonfly';
ver data3 = {name: 'Bobby', age: 20000, job: 'dj'};
Then you just add to that set using its add method.
mySet.add(data1);
mySet.add(data2);
mySet.add(data3);
So to get what you are looking for you would write this.
var itms = {items: [{name: 'test'}]};
mySet.add(itms);
The good thing about set is that is like an array. So you can use forEach.
mySet.forEach( function(val){
console.log(val); // gets all your data.
});
You can even check if a value is in your data using the has method.
mySet.has(365); // true
mySet.has(36500000); as false
JavaScript Set

Categories

Resources