Update/Create object properties dynamically - javascript

I'm creating a feature that provides a get() and set() function to read and update values from a JavaScript object. The trick is that I'm using a dot notated string to specify the properties.
I've got the get() working fine, it's the set() that I'm having troubles with.
Sample data:
var data = {
person: {
name: 'Fred',
birthday: '19840101',
address: {
street: '123 Main St.',
city: 'Anytown',
state: 'NY',
zip: '123123'
},
family: {
spouse: 'Sally',
children: ['Sam', 'Frank', 'Susan']
}
}
};
get() function:
function get (path) {
var parts = path.split('.'),
tmp = data;
for (var i = 0, l = parts.length; i < l; ++i) {
if (tmp[parts[i]]) {
tmp = tmp[parts[i]];
}
else {
tmp = null
break;
}
}
return tmp;
}
console.log(get('person.name')); // Fred
console.log(get('person.family.children')); // ['Sam', 'Frank', 'Susan']
console.log(get('person.jobs.apple')); // null
set() function:
var foo = null;
function set (path, val) {
var parts = path.split('.')
tmp = {},
foo = data;
for (var i = 0, l = parts.length; i < l; ++i) {
if (tmp[parts[i]]) {
tmp = data[parts[i]];
}
else {
tmp = {};
}
if (i+1 === l) {
tmp = val;
}
data[parts[i]] = tmp;
}
}
set('person.name', 'Dave'); // replace existing name
set('person.family.children', ['Tim', 'Matt', 'Kathy']); // replace existing array
set('person.jobs.apple', 'developer'); // should create the necessary properties
console.log(data);
I end up with the following object:
obj = {
person: {},
name: "Dave",
family: {},
children: ["Tim","Matt","Kathy"],
jobs: {},
apple: "developer"
}
Any thoughts on how to accomplish this?

You do not really need any functions at all for this.
See this
var data = {
person: {
name: 'Fred',
birthday: '19840101',
address: {
street: '123 Main St.',
city: 'Anytown',
state: 'NY',
zip: '123123'
},
family: {
spouse: 'Sally',
children: ['Sam', 'Frank', 'Susan']
}
}
};
alert(data.person.name);
data['person']['name'] = 'Tim';
alert(data['person']['name']);
There is also this answer which may help
Javascript object key value coding. Dynamically setting a nested value

It appears that you have some typos in your set code.
The line tmp = data[parts[i]]; should really be tmp = tmp[parts[i]];.
And then you probably want to put that into a new variable - say, newTmp, - so that you can assign it back into the object later:
tmp[parts[i]] = newTmp;
tmp = newTmp;

Your problem seems to be the data[parts[i]] = tmp; at the end of the set function.

Related

Can't access object's properties by argument of a function

Why array[i].key (where key === "surname") within the function doesn't work,
meanwhile array[i].surname works perfectly?
let objects = [
{ name: 'Jack', surname: 'Jackson' },
{ name: 'Ivar', surname: 'Bjornsson' },
{ name: 'John', surname: 'Mickelson' }
];
function sort (array, key) {
for (let i = 0; i < array.length; i++) {
console.log(array[i].key)// Somehow the "key", which is equal to "surname" doesn't work;
// here will be undefined;
console.log(array[i].surname)//But here writing 'surname' directly works fine;
// the correct answer will be there;
console.log(key)// However, key === surname
}
}
sort(objects, 'surname');
You have to access the property with square brackets:
let objects = [
{ name: 'Jack', surname: 'Jackson' },
{ name: 'Ivar', surname: 'Bjornsson' },
{ name: 'John', surname: 'Mickelson' }
];
function sort (array, key) {
for (let i = 0; i < array.length; i++) {
console.log(array[i][key])// Somehow the "key", which is equal to "surname" doesn't work;
// here will be undefined;
console.log(array[i].surname)//But here writing 'surname' directly works fine;
// the correct answer will be there;
console.log(key)// However, key === surname
}
}
sort(objects, 'surname');
This
array[i].key
is equivalent to
array[i]['key']

Iterate through Array of objects and if all key values equal the same return true else false Javascript

I have the below array of objects I basically want to compare the values in the language keys and if all are the same return true else return false. Any ideas would be appreciated.
var list1 = [
{ firstName: 'Daniel', lastName: 'J.', country: 'Aruba', continent: 'Americas', age: 42, language: 'JavaScript' },
{ firstName: 'Kseniya', lastName: 'T.', country: 'Belarus', continent: 'Europe', age: 22, language: 'JavaScript' },
{ firstName: 'Hanna', lastName: 'L.', country: 'Hungary', continent: 'Europe', age: 65, language: 'JavaScript' },
];
function isSameLanguage(list) {
let counter =0;
for (let i=0; i<=list.length; i++){
counter = counter = counter +1
if (list.language[i] == list.language[i]){
//console.log("Match")
}
}
}
What you're looking for the is the every method. In your case it'd look something like this:
function isSameLanguage(list) {
if (!list.length) return false; // guard to prevent the next line from throwing an error
const firstLanguage = list[0].language;
return list.every(item => item.language === firstLanguage);
}
You don't need a counter variable.
list.language[i] == list.language[i] in the context of your data doesn't make sense. It's the index of the object, not the index of the language. So: list[i].language, and you can compare that to the next element in the array: list[i].language !== list[i + 1].language. If they don't match return from the loop ("No match"), otherwise, wait for the loop to complete ("Match").
Example one where they all do match:
const list1=[{firstName:"Daniel",lastName:"J.",country:"Aruba",continent:"Americas",age:42,language:"JavaScript"},{firstName:"Kseniya",lastName:"T.",country:"Belarus",continent:"Europe",age:22,language:"JavaScript"},{firstName:"Hanna",lastName:"L.",country:"Hungary",continent:"Europe",age:65,language:"JavaScript"}];
function isSameLanguage(list) {
for (let i = 0; i < list.length - 1; i++) {
if (list[i].language !== list[i + 1].language) {
return 'No match';
}
}
return 'Match';
}
console.log(isSameLanguage(list1));
Example two where they don't:
const list1=[{firstName:"Daniel",lastName:"J.",country:"Aruba",continent:"Americas",age:42,language:"JavaScript"},{firstName:"Kseniya",lastName:"T.",country:"Belarus",continent:"Europe",age:22,language:"JavaScript"},{firstName:"Hanna",lastName:"L.",country:"Hungary",continent:"Europe",age:65,language:"JavaScript2"}];
function isSameLanguage(list) {
for (let i = 0; i < list.length - 1; i++) {
if (list[i].language !== list[i + 1].language) {
return 'No match';
}
}
return 'Match';
}
console.log(isSameLanguage(list1));

Filter the object depending on the field's value in javascript

I'm kinda new to javascript. Here I have the following object:
obj = {
0:{id:1, location: loc1, title:title1},
1:{id:2, location: loc2, title:title2},
2:{id:3, location: loc1, title:title3},
3:{id:4, location: loc3, title:title4},
4:{id:5, location: loc1, title:title5}
}
What I need is to filter the object by location depending on its value and create a new object like the following:
obj = {
loc1:{
0:{id:1, location: loc1, title:title1},
1:{id:3, location: loc1, title:title3},
2:{id:5, location: loc1, title:title5}
},
loc2:{
0:{id:2, location: loc2, title:title2}
}
loc3:{
0:{id:4, location: loc3, title:title4}
}
}
How can I achieve the above object?
I tried using for and push to a new array but the location should be dynamic and may change in the future and I want to have one object to manage like above.
var theLoc1 = [], theLoc2 = [];
for(var i = 0; i < response.length; i++) {
if(response[i].location == 'loc1'){
theLoc1.push(response[i]);
}else if(response[i].location == 'loc2'){
theLoc2.push(response[i]);
}
}
This Code is what u really need:
obj = [
{ id: 1, location: 'loc1', title: 'title1' },
{ id: 2, location: 'loc2', title: 'title2' },
{ id: 3, location: 'loc1', title: 'title3' },
{ id: 4, location: 'loc3', title: 'title4' },
{ id: 5, location: 'loc1', title: 'title5' }
];
var locations = {};
for (var i = 0; i < obj.length; i++) {
locations[obj[i].location] = [];
}
console.log(locations);
for (var i = 0; i < obj.length; i++) {
locations[obj[i].location].push(obj[i]);
}
console.log(locations);
**Update:It Can be done in a single for loop but for simplicity reasons i wrote it like this. **
let obj;
for(var i = 0; i < response.length; i++) {
if( !Object.hasOwnProperty(obj, response[i].location)
{ obj[response[i].location] = []; }
obj[response[i].location].push(response[i]);
}
You can dynamically create JS object properties if you just address them. This means:
let obj = {};
obj.bark = "how-how";
console.log(obj.bark); // "how-how";
obj[bark2] = "waf-waf";
console.log(obj.bark2); // "waf-waf";
you can use it to struct your new object with the locations names, so even if someday you get "location999" it'll still work.
I put the if that checks if the object laready has that property because you want the property to be an array. If it wasn't you could've just put the value inside like in my example, but im not sure if push would work on it so I initialize it to be empty array just in case. You can check it yourself and ommit the if if its not needed.
My solution using functional programming.
const obj = {
0: { id: 1, location: 'loc1', title: 'title1' },
1: { id: 2, location: 'loc2', title: 'title2' },
2: { id: 3, location: 'loc1', title: 'title3' },
3: { id: 4, location: 'loc3', title: 'title4' },
4: { id: 5, location: 'loc1', title: 'title5' }
};
const result = Object.keys(obj).reduce((newObject, item) => {
const location = obj[item].location;
const index = newObject[location] ? Object.keys(newObject[location]).length : 0;
return {
...newObject,
[location]: {
...newObject[location],
[index]: obj[item]
}
};
}, {});
console.log(result);
In order to group your items by location you can iterate your array, see whether its location was already grouped and if not, create a new group for it. Afterwards add the item to the corresponding group.
var obj = [
{id: 1, location: "loc1", title: "title1"},
{id: 2, location: "loc2", title: "title2"},
{id: 3, location: "loc1", title: "title3"},
{id: 4, location: "loc3", title: "title4"},
{id: 5, location: "loc1", title: "title5"}
];
var formattedArray = new Array();
for (var i = 0; i < obj.length; i++) {
if (!formattedArray[obj[i].location]) {
formattedArray[obj[i].location] = new Array();
}
formattedArray[obj[i].location].push(obj[i]);
}
console.log(formattedArray);
JsFiddle example code:
JsFiddle
You can try the following if your loc1, loc2, loc3 are fixed. (That is what I understood after reading your query)
var response = [
{id:1, location: "loc1", title:"title1"},
{id:2, location: "loc2", title:"title2"},
{id:3, location: "loc1", title:"title3"},
{id:4, location: "loc3", title:"title4"},
{id:5, location: "loc1", title:"title5"}
]
var resObj = {
published:[],
private: [],
pending:[]
}
for(var i = 0; i < response.length; i++) {
if(response[i].location == 'loc1'){
resObj.published.push(response[i]);
}else if(response[i].location == 'loc2'){
resObj.private.push(response[i]);
}else {
resObj.pending.push(response[i]);
}
}
console.log(resObj)
I think the better way to do this, is to group your objects already in your backend. You can use the linq function .GroupBy(x => x.location).
This is near the same problem:
How to count rows of a table grouped by shortdatestring?
// create an array of arrays;
var groupOfLocations[];
// loop on your locations
for(var i = 0; i < response.length; i++) {
// push if already existing
for(var iGroup = 0; iGroup < groupOfLocations.length; iGroup++) {
if(groupOfLocations[iGroup][0].location == response[i].location) {
groupOfLocations[iGroup].push(response[i]); break;
}
// create a new array if not found
if(iGroup >= groupOfLocations.length) groupOfLocations.push(new array(response[i]));
}
May contains syntax mistakes, but the idea is here.

How can I delete a key in a nested Javascript object with a dynamic number of properties and without eval?

I am looking to delete a specific key from a nested Javascript object based on a list of dynamic properties. Here is an example of what I mean:
This is a sample object:
employees: [
{
name: "John",
id: 1234567890,
salary: 60000
},
{
name: "Jack",
id: 0987654321,
salary: 55000
}
],
location: {
building: {
address: "111 Main St"
}
}
I am looking to delete the address key when I am provided an array of ['location', 'building', 'address']
When I say "dynamic" I mean that I could also be provided with an array of ['employees', 1] so I cannot rely on a set number of nested properties.
The only approach that works for me right now is to use the dreaded eval, which is not a permanent solution since the Javascript objects that I am reading are written by users.
let jsObject = ... // the object shown above
let properties = ['location', 'building', 'address']
let evalString = ''
for (let i = 0; i < properties.length; i++){
evalString += '[\''+properties[i]+'\']'
}
eval('delete jsObject'+evalString)
What is an alternative to eval that will accomplish this same goal?
You could reduce the object by the keys and save the last key for deleting the object with that key.
function deleteKey(object, keys) {
var last = keys.pop();
delete keys.reduce((o, k) => o[k], object)[last];
return object;
}
var object = { employees: [{ name: "John", id: '1234567890', salary: 60000 }, { name: "Jack", id: '0987654321', salary: 55000 }], location: { building: { address: "111 Main St" } } };
console.log(deleteKey(object, ['location', 'building', 'address']));
.as-console-wrapper { max-height: 100% !important; top: 0; }
This method accepts an object and an array of properties, and removes the inner most property as required
function remove(obj, props) {
delete props.slice(0, -1).reduce((init, curr) => init && init[curr], obj)[[...props].pop()];
}
You can break your array into everything except the last element, get a reference to that and the call delete on the object using the last element. You can use reduce to easily build the object reference. You need to be careful with arrays because you can't use delete without leaving an empty slot — delete doesn't change the length.
Here's the basic idea:
function deleteProp(obj, keys){
let prop = keys.pop() // get last key
let c = keys.reduce((a, c) => a[c], obj) // get penultimate obj
if (Array.isArray(c)) c.splice(prop, 1) // if it's an array, slice
else delete c[prop] // otherwise delete
}
// Delete address
let obj = {employees: [{name: "John",id: 1234567890,salary: 60000},{name: "Jack",id: 0987654321,salary: 55000}],location: {building: {address: "111 Main St"}}}
deleteProp(obj, ['location', 'building', 'address'])
console.log(obj)
//Delete employee 1
obj = {employees: [{name: "John",id: 1234567890,salary: 60000},{name: "Jack",id: 0987654321,salary: 55000}],location: {building: {address: "111 Main St"}}}
deleteProp(obj, ['employees', 1])
console.log(obj)
//Delete employee 1 id
obj = {employees: [{name: "John",id: 1234567890,salary: 60000},{name: "Jack",id: 0987654321,salary: 55000}],location: {building: {address: "111 Main St"}}}
deleteProp(obj, ['employees', 1, 'id'])
console.log(obj)
Here is a sample that i'm sure could be trimmed down a bit but it explains each step and you should be able to see whats happening in it:
let jsObject = {
employees: [{
name: "John",
id: 1234567890,
salary: 60000
},
{
name: "Jack",
id: 0987654321,
salary: 55000
}
],
location: {
building: {
address: "111 Main St"
}
}
};
let properties = ['location', 'building', 'address'];
// we use this to traverse the object storing the parent
let parent = null;
// run over each property in the array
for (let i = 0; i < properties.length; i++) {
// check if this is the last property and we have the parent object
if (i + 1 == properties.length && parent)
delete parent[properties[i]]; // just delete the property from the object
else if (parent === null)
parent = jsObject[properties[i]] // set the initial parent
else
parent = parent[properties[i]] // set the parent to the property in the existing object
}
// log the output
console.log(jsObject);
You are going to want to throw error handling and checks to make sure you don't end up outside the object as well.
Navigate to the object that contains the property you want to delete, then delete it:
let jsObject = {
employees: [
{
name: "John",
id: 1234567890,
salary: 60000
},
{
name: "Jack",
id: 0987654321,
salary: 55000
}
],
location: {
building: {
address: "111 Main St"
}
}
};
let properties = ['location', 'building', 'address'];
let innerMost = jsObject;
for (let i = 0; i < properties.length - 1; i++) {
if (typeof innerMost !== "object" || innerMost === null) {
innerMost = null;
break;
}
innerMost = innerMost[properties[i]];
};
if (
innerMost !== null &&
typeof innerMost === "object" &&
properties.length > 0 &&
innerMost.hasOwnProperty(properties[properties.length - 1])
) {
delete innerMost[properties[properties.length - 1]];
console.log(jsObject);
} else console.log("Invalid path");

Loop through an array of objects and sort them

I have an array containing some objects and I am trying to loop through it where I have data stored in the following order:
firstName: Alice
lastName: Wonderland
age:12
I am trying to loop, then to sort it in descending order where age: value should be in first position then > lastName: Wonderland comes and lastly firstName.
Here is my code until this moment
var data = {
example1: [{
firstName: 'Alice',
lastName: 'Wonderland',
age: 12
}],
example2: [{
firstName: 'Thomas',
lastName: 'Mathison',
age: 14
}],
example3: [{
firstName: 'David',
lastName: 'Jacobsen',
age: 18
}]
};
for (var key in data) {
var arr = data[key];
for (var i = 0; i < arr.length; i++) {
var obj = arr[i];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
console.log(prop + ': ' + obj[prop]);
}
}
}
}
I want to achieve the reverse order (descending) when I output the result in the console.log();:
age: 12,
lastName: 'Wonderland',
firstName: 'Alice'
age:14,
lastName: 'Mathison',
firstName: 'Thomas'
age:18,
lastName: 'Jacobsen',
firstName: 'David'
I am not sure about the sort function behavior. How should it work during the loop?
Any suggestions?
var data = {
example1: [{
firstName: 'Alice',
lastName: 'Wonderland',
age: 12
}],
example2: [{
firstName: 'Thomas',
lastName: 'Mathison',
age: 14
}],
example3: [{
firstName: 'David',
lastName: 'Jacobsen',
age: 18
}]
};
var objectArray=[];
for (var key in data) {
var arr = data[key];
for (var i = 0; i < arr.length; i++) {
var obj = arr[i];
objectArray.push(obj);
}
}
objectArray.sort(function(element1,element2){
return element2.age - element1.age
}); //now iterate over the array it is sorted in descending order
Sorting arrays of non-primitive data types (custom objects and data structures like in your case) require two steps. It's quite straightforward so follow along.
First you need to create a function capable of comparing two objects of your custom data structure according to your desired criteria.
Second, you provide this decision function to a sort function along with your array and it will use it to sort the array for you. Lets do it for your case:
First the compare function, a and b are objects from your custom structure. returning 1 means object a is "bigger", returning -1 means b is "bigger", returning 0 means that, according to your criteria, both are equal in "size". The order of the if statements bellow is naturally important and reflects the priorities you described:
age takes priority over names and last-name over first-name.
function compare_people(a, b) {
if (a.age < b.age) {
return -1;
}
if (a.age > b.age) {
return 1;
}
if (a.lastName < b.lastName) {
return -1;
}
if (a.lastName > b.lastName) {
return 1;
}
if (a.firstName< b.firstName) {
return -1;
}
if (a.firstName> b.firstName) {
return 1;
}
return 0;
}
Now all you have to do is provide your criteria and array to javascript's sort function. In your case objects are stored inside the data array, so you do:
data.sort(compare_people);
Done, array sorted!
Here you can study the concept more in depth https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
Good luck.
Apparently the question is not clear enough, people keep giving you sorting algorithms which I understand it is not what you are looking for, you want to change the internal order of the properties (which makes no sense, they have no 'order' they are part of a map, in any case, here is what I would do:
var data = {
example1: [{
firstName: 'Alice',
lastName: 'Wonderland',
age: 12
}],
example2: [{
firstName: 'Thomas',
lastName: 'Mathison',
age: 14
}],
example3: [{
firstName: 'David',
lastName: 'Jacobsen',
age: 18
}]
};
for (var key in data) {
var arr = data[key];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
var obj = arr[i];
newArr.push({
age: obj.age,
firstName: obj.firstName,
lastName: obj.lastName
})
}
data[key] = newArr;
}
But again, what you are trying to do makes no sense, or at least according to the description.
Use [].unshift() method
var result = [];
for (var key in data) {
var arr = data[key];
for (var i = 0; i < arr.length; i++) {
var obj = arr[i];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
result.unshift(prop + ': ' + obj[prop])
}
}
}
}
console.log(result)
here is demo https://plnkr.co/edit/N4Zt28zh0A3MpwoOrzmZ?p=preview

Categories

Resources