Renaming or setting object names dynamically in JavaScript - javascript

I'm fairly new to JavaScript I setup below to use keys and values for different items I need to work. All the values will be the same using a variable to distinguish each item (which will just be it, but I would like to identify each item (itemObject) which I'm also using for the variable name.
Abbreviated example::
function objFunction (itemName) {
itemObject = {
object1Data: [{
data1: {
prop1: 'value',
prop2: 'value'
},
data2: {
prop1: itemName + 'some string'
prop2: itemNamevariable
}
}],
object2: {
data: {
prop1: itemName,
prop2: itemName + 'some string'
}
}
}
}
Or am I better off using a constructor or another method?
function ObjFunction (itemName) {
this.itemObject = {
bject1Data: [{
data1: {
prop1: 'value',
prop2: 'value'
},
data2: {
prop1: itemName + 'some string',
prop2: itemName
}
}],
object2: {
data: {
prop1: itemName,
prop2: itemName + 'some string'
}
}
}
}
var item1 = new ObjFunction('item1Name');

I prefer the first answer, just change the second line from:
itemObject = {
to
return {
and then you can say:
var item1 = objFunction('item1Name');
In other words:
function objFunction (itemName) {
return {'name' : itemName};
}
var y = objFunction("theName");
console.log(y.name);

Related

Create an array based on two objects

I have two objects, that contains same number of properties:
object1: {
prop1: 'data12',
prop2: 'data13',
prop3: 'data58',
prop4: 'xyz',
// more props here...
}
object2: {
prop1: 123,
prop2: 'email#gmail.com',
prop3: 'text',
prop4: 'bla',
// more props here...
}
I want to build an array {name: '', value: ''} based on this objects:
[
{ data12: 123 },
{ data13: 'email#gmail.com' },
{ data58: text },
{ xyz: 'bla' },
]
How can you do that? The number of properties could be different.
Just using Object.keys() can do it
let object1 = {
prop1: 'data12',
prop2: 'data13',
prop3: 'data58',
prop4: 'xyz',
}
let object2 = {
prop1: '123',
prop2: 'email#gmail.com',
prop3: 'text',
prop4: 'bla',
}
let result =[]
let obj = {}
Object.keys(object1).forEach(k =>{
obj[object1[k]] = object2[k]
})
result.push(obj)
// combined with reduce can also do it
/*
let obj =Object.keys(object1).reduce((a,c) => {
a[object1[c]] = object2[c]
return a
},{})
*/
result.push(obj)
console.log(result)

Transform JavaScript object by clearing, adding and removing properties

Here are the instructions for what I am supposed to do:
Implement a function accepting 2 arguments: state and actions. The
function should change the state basing on the given actions
array.
state is an object. You are supposed to add, change, or delete its properties instead of creating a new object
actions is an array of objects. Each object in this array has the next properties:
type contains a string: either 'addProperties', 'removeProperties' or 'clear';
The second property of each object depends on type and may be one of the following:
if type is addProperties, second property is extraData. It contains an object
with key: value pairs to add to the state;
if type is removeProperties, second property is keysToRemove. It contains an array
with the list of property names (keys) to remove from the state; (Not existing
properties should be ignored)
if type is clear you should remove all the properties from the
state. No second property in this case;
Example of usage:
If state is {foo: 'bar', bar: 'foo'}, then
transformState(state, [
{
type: 'addProperties',
extraData: {
name: 'Jim',
hello: 'world',
}
},
{
type: 'removeProperties',
keysToRemove: ['bar', 'hello'],
},
{
type: 'addProperties',
extraData: { another: 'one' },
}
])
should modify the state, doing the following:
add two properties to the state
then remove keys bar and hello from the state
and finally add another one property to the state
After these operations the object state will have the following look
{
foo: 'bar',
name: 'Jim',
another: 'one',
}
Another example:
const state = { x: 1 };
transformState(state, [
{ type: 'addProperties', extraData: { yet: 'another property' } }
{ type: 'clear' },
{ type: 'addProperties', extraData: { foo: 'bar', name: 'Jim' } }
]);
state === { foo: 'bar', name: 'Jim' }
Here is my code so far:
function transformState(state, actions) {
const transformedState = state;
for (const { type } of actions) {
if (type === 'clear') {
Object.keys(transformedState)
.forEach(key => delete transformedState[key]);
} else if (type === 'addProperties') {
for (const { extraData } of actions) {
for (const data in extraData) {
transformedState[data] = extraData[data];
}
}
} else if (type === 'removeProperties') {
for (const { keysToRemove } of actions) {
for (const item in keysToRemove) {
delete transformedState[keysToRemove[item]];
}
}
}
}
return transformedState;
}
It works with almost all scenarios, but it doesn't remove some properties and I can not figure out why. The 2 test it won't pass:
test('Should apply several types', () => {
const state = {
foo: 'bar', bar: 'foo',
};
transformState(state, [
{
type: 'addProperties',
extraData: {
name: 'Jim', hello: 'world',
},
},
{
type: 'removeProperties', keysToRemove: ['bar', 'hello'],
},
{
type: 'addProperties', extraData: { another: 'one' },
},
]);
expect(state)
.toEqual({
foo: 'bar', name: 'Jim', another: 'one',
});
});
test('Should work with a long list of types', () => {
const state = {
foo: 'bar', name: 'Jim', another: 'one',
};
transformState(state, [
{
type: 'removeProperties', keysToRemove: ['another'],
},
{ type: 'clear' },
{ type: 'clear' },
{ type: 'clear' },
{
type: 'addProperties', extraData: { yet: 'another property' },
},
{ type: 'clear' },
{
type: 'addProperties',
extraData: {
foo: 'bar', name: 'Jim',
},
},
{
type: 'removeProperties', keysToRemove: ['name', 'hello'],
},
]);
expect(state)
.toEqual({ foo: 'bar' });
});
In the first test in the add properties section, if I change the order of "hello" and "name", it still only removes name and I can't figure out why. Here is the test output:
● Should apply several types
expect(received).toEqual(expected) // deep equality
- Expected
+ Received
Object {
"another": "one",
"foo": "bar",
- "name": "Jim",
+ "hello": "world",
}
196 |
197 | expect(state)
> 198 | .toEqual({
| ^
199 | foo: 'bar', name: 'Jim', another: 'one',
200 | });
201 | });
at Object.<anonymous> (src/transformState.test.js:198:6)
● Should work with a long list of types
expect(received).toEqual(expected) // deep equality
- Expected
+ Received
Object {
"foo": "bar",
+ "yet": "another property",
}
229 |
230 | expect(state)
> 231 | .toEqual({ foo: 'bar' });
| ^
232 | });
233 |
at Object.<anonymous> (src/transformState.test.js:231:6)

Filter array by object property values

I am programming a function that will handle javascript array filtering. I know the values by which I want to filter so I know how to do it in a fairly easy way, but I would like the code to be more extensible.
I wrote such a function:
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = { prop2: 'Paris', prop3: '987' };
const handleFilters = (items, filters) => {
return items.filter((item) => {
if (filters.prop3 && filters.prop2) {
return item.prop3 === filters.prop3 && item.prop2 === filters.prop2;
}
if (filters.prop3) {
return item.prop3 === filters.prop3;
}
if (filters.prop2) {
return item.prop2 === filters.prop2;
}
});
}
I am not completely satisfied with it. I think it could be written better. If the 3rd argument comes, I don't want to add it to the if - it should be automatic.
I've searched several topics on stackoverflow, looked through the lodash documentation looking for some good solution but I have no idea what I can do better with this.
If I understood correctly, you want to only keep the items that match all the filter:
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = {prop2: 'Paris', prop3: '987'};
const handleFilters = (items, filters) => {
return items.filter((item) =>
Object.entries(filters).every(([key, val]) => item[key] === val)
)
};
console.log(handleFilters(items, filters))
This basically checks that every (key, val) of the filter exists in the item
private handleFilters (items, ...props) {
return items.filter(item => props.every(prop => item[prop] === prop));
}
To use the filter object dynamically, you could look into using something like Object.keys().
Here's your example code changed to use that:
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = { prop2: 'Paris', prop3: '987' };
const handleFilters = (items, filters) => {
return items.filter(item => {
return Object.keys(filters).some(filterKey => item[filterKey] === filters[filterKey])
});
}
It loops through the items, the same as you already were doing. However, now it also loops through the keys set in the filters object. The some() function returns a boolean; true if the callback is true and false if the callback is false. Here it checks if the value in item[filterKey] is the same as the value in filters[filterKey].
It's important to note that this returns the item if any of the filter values matches with an item's property. If all values must be in the filtered item, you'll want to use Object.keys(filters).every(filterKey => item[filterKey] === filters[filterKey]).
The every function returns true only if all filterKeys have the same value as the keys checked in item.
It is also possible to use Object.entries(), which allows directly using the value, but for this example I chose to use just the keys, for consistency.
You can filter by using Object.key , filter and every method in precise manner.
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = { prop2: 'Paris', prop3: '987' };
const handleFilter = (items, filters) => {
return items.filter((item) => Object.keys(filters).every((key) => item[key] === filters[key]));
}
console.log(handleFilter(items, filters))
You can remove your multiple if blocks by using for..in
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = { prop2: 'Paris', prop3: '987' };
const handleFilters = (items, filters) => {
return items.filter((item) => {
for(const key in filters) {
if (filters[key] !== item[key]) {
return false;
}
}
return true;
});
}
console.log(handleFilters(items, filters))
It does the same as your code was doing.
Some Improvement over #David Alvarez approach, here i have used Object.keys instead of Object.entries as Object.key is faster than Object.entries here is the comparison: https://www.measurethat.net/Benchmarks/Show/3685/0/objectentries-vs-objectkeys-vs-objectkeys-with-extra-ar
const items = [
{
prop1: 'Jotahan',
prop2: 'London',
prop3: '1234567'
},
{
prop1: 'Jones',
prop2: 'Paris',
prop3: '987'
}
];
const filters = {prop2: 'Paris', prop3: '987'};
const handleFilters = (items, filters) => (
items.filter((item) =>
Object.keys(filters).every(key => item[key] === filters[key])
)
);
console.log(handleFilters(items, filters))

Angular 11 can't access object property dynamically

I have to access to a subset of my object properties and store the label and the value in another array. Basically I have this object:
myObject: CustomObject {
prop1: value1,
prop2: value2
prop3: value3
}
and as output I need an array with objects of type: {label: string, value: number}.
I need to fill this array with prop1 and prop2 and their values, so that I have something like this:
myArray = [{label: prop1, value: value1}, {label: prop2, value: value2}]
What I've tried is this:
labels = ['prop1', 'prop2'];
labels.forEach((l: any) => {
this.myArray.push({ label: l, value: this.myObject[l] })
})
or also this.myObject.l
But I get this error:
Element implicitly has an 'any' type because expression of type 'any'
can't be used to index type 'MyObject'
I've changed the type of "l" to string but I got:
No index signature with a parameter of type 'string' was found on type
What's the correct way to do this?
myObject
Instead of any, you can use keyof to tell typescript that labels is an array of strings where strings are the keys of your interface:
Playground Link
interface CustomObject {
prop1: any;
prop2: any;
prop3: any;
}
const myObject: CustomObject = {
prop1: 1,
prop2: 2,
prop3: 3,
}
const labels: (keyof CustomObject)[] = ['prop1', 'prop2'];
const myArray = labels.map(label => ({ label, value: myObject[label] }))
console.log(myArray)
You can use Object.entries function to iterate over key/value pairs:
const myObject = { prop1: 'value1', prop2: 'value2', prop3: 'value3' }
let myArray = [];
Object.entries(myObject).map((item)=>{ myArray.push({label:item[0], value:item[1]})})
console.log(myArray)

Remove objects from a deeply nested object array by property value

Consider I have a nested object array. One possible example scenario could be:
content: [
{
prop1: someValue,
prop2: someValue,
content: [
{
prop2: someValue,
prop3: someValue,
myProperty: myValue
},
{
prop1: someValue,
prop3: someValue,
myProperty: otherValue
}
]
},
{
prop5: someValue,
prop2: someValue
}
]
Here are the possibilities:
The structure starts with content[] but the descendants may or may not have content property.
The level of the hierarchy can be of any number.
The properties contained by the objects are not always the same i.e. one object may have x, y, z properties while the other may have v, w, z properties.
If any object in the hierarchy has myProperty key, there won't be content key.
More than one object in the hierarchy can have myProperty with value'myValue.
My requirement:
If at any level an object has the property myProperty with the value myValue then remove the entire object (NOT JUST THE PROPERTY) from the hierarchy.
My attempt so far:
private removeObjects(content: any, values: string[]): any {
if (!content || content.length === 0) {
return
}
content = content.filter((c) => {
if (!c.myProperty) return true
return c.myProperty.indexOf(values) > 0
})
// Here is my problem since I am supposed to do a recursive call on each of child contents,
// how do I merge back the original array?
return this.removeObjects(content, values)
}
The following recursively returns a new array without mutating the original
const content = [{
prop1: "someValue",
prop2: "someValue",
content: [{
prop2: "someValue",
prop3: "someValue",
myProperty: "myValue"
},
{
prop1: "someValue",
prop3: "someValue",
myProperty: "otherValue"
}
]
},
{
prop5: "someValue",
prop2: "someValue"
}
]
function removeObjects(content) {
return content.reduce((arr, obj) => {
if (obj["myProperty"] && obj["myProperty"] === "myValue") {
return arr
} else if (obj["content"] && obj["content"].length) {
arr.push({ ...obj,
content: removeObjects(obj["content"])
})
return arr
} else {
arr.push(obj);
return arr;
}
}, []);
}
console.log(removeObjects(content))
Expected output:
const content = [{
prop1: "someValue",
prop2: "someValue",
content: [
{
prop1: "someValue",
prop3: "someValue",
myProperty: "otherValue"
}
]
},
{
prop5: "someValue",
prop2: "someValue"
}
]
You can use below function to get expected result:
let data = {
content: [
{
prop1: 'someValue',
prop2: 'someValue',
content: [
{
prop2: 'someValue',
prop3: 'someValue',
myProperty: 'myValue'
},
{
prop1: 'someValue',
prop3: 'someValue',
myProperty: 'otherValue'
}
]
},
{
prop5: 'someValue',
prop2: 'someValue'
}
]
}
function removeMyvalyeObj(data) {
for (let i = data.content.length - 1; i >= 0; i--) {
if (data.content[i].myProperty === 'myValue') {
data.content.splice(i, 1);
} else if(data.content[i].content) {
removeMyvalyeObj(data.content[i])
}
}
}
removeMyvalyeObj(data);
console.log(data);
Try this, (this is JavaScript version)
let someValue = 'a';
let otherValue ='x';
let myValue = 'xy';
let content = [
{
prop1: someValue,
prop2: someValue,
content: [
{
prop2: someValue,
prop3: someValue,
myProperty: myValue
},
{
prop1: someValue,
prop3: someValue,
myProperty: otherValue
}
]
},
{
prop5: someValue,
prop2: someValue
}
];
function removeObjects(content, values) {
debugger;
if (!content || content.length === 0) {
return
}
content = content.filter((c) => {
if (!c.myProperty) return true
return c.myProperty.indexOf(values) > 0
})
// Here is my problem since I am supposed to do a recursive call on each of child contents,
// how do I merge back the original array?
return content.map(x => ({
...x,
content: removeObjects(x.content, values)
}))
}
console.log(removeObjects(content, 'x'))
Use recursive approach, to have filter array of items and their content array.
const filter = (arr, name, value) =>
arr
.filter((obj) => !(name in obj && obj[name] === value))
.map((obj) => {
const nObj = { ...obj };
if (obj.content && Array.isArray(obj.content)) {
nObj.content = filter(obj.content, name, value);
}
return nObj;
});
content = [
{
prop1: 'someValue',
prop2: 'someValue',
content: [
{
prop2: 'someValue',
prop3: 'someValue',
myProperty: 'myValue',
},
{
prop1: 'someValue',
prop3: 'someValue',
myProperty: 'otherValue',
},
],
},
{
prop5: 'someValue',
prop2: 'someValue',
},
];
const updated = filter(content, "myProperty", "myValue");
console.log(updated);

Categories

Resources