Convert key value pair to array of objects - javascript

I have a key value pair:
`{good: 'value1', key2: 'value2': key3: 'value3'}
I want to convert it as the following:
[{key: 'good', value:'value1'}, {key: 'key2', value: 'value2'}, {key: 'key3', value: 'value3']
So far, I am able to convert them into an array with Object.entries, but I am unable to get my desired result.

There exists a method Object.entries that turns object into list of keys and values already, mapping it to match your required format should not be difficult.
const data = {good: 'value1', key2: 'value2', key3: 'value3'};
const result = Object.entries(data).map(([key, value]) => ({key, value}))
console.log(result)

You can do it like this:
const data = {good: 'value1', key2: 'value2', key3: 'value3'};
const result = [];
Object.keys(data).forEach(key => {
result.push({key, value: data[key]})
})
console.log(result)

To transform arrays, javascript provides a variety of array methods. map, foreach, reduce. etc. (Read more)
Object.entries( data ) converts your object into an array of arrays where each inner array is a key-value pair like this
[ [key1, value1], [key2, value2] ....]. (Read More)
Your usecase:
const data = { good: 'value1', key2: 'value2', key3: 'value3' };
const entries = Object.entries(data);
// [ ["good","value1"], ["key2","value2"],["key3","value3"] ]
// Map those entries to your desired form.
const results = entries.map( entry => ({ key: entry[0], value: entry[1] }) ) ;
// Or leverage the destructuring fetaure
// const results = entries.map( ([key, value]) => ({ key, value }) ) ;
console.log(results)

In case if you are interested in fast solution:
type Values<T> = T[keyof T]
type Mapper<T> = {
[Prop in keyof T]: { key: Prop, value: T[Prop] }
}
type Convert<T> = Array<Values<Mapper<T>>>
function convert<
Prop extends string,
Value extends string,
Obj extends Record<Prop, Value>
>(obj: Obj): Convert<Obj>
function convert<
Prop extends string,
Value extends string,
Obj extends Record<Prop, Value>
>(obj: Obj) {
const result: {
key: Extract<keyof Obj, string>;
value: Obj[Extract<keyof Obj, string>];
}[] = []
for (let prop in obj) {
result.push({
key: prop,
value: obj[prop]
})
}
return result
}
const result = convert({ good: 'value1', key2: 'value2', key3: 'value3' })
const first = result[0]
if (first.key === 'good') {
// {
// key: "good";
// value: "value1";
// }
first
}
Cons:
convert function creates internal result variable and mutates it. It is possible to use recursion instead of mutation result but I'm not sure this trade of worth it.
Pros
Faster than entries & map
Convert types maps type of argument to the type you want to achieve but only in type scope, whereas function maps argument in runtime scope.
If performance is not critical, you probably should stick with other solution where Object.entries is used

Related

Filter nested object

I receive an object that looks like this:
this.tokensData = {
O: {
id: 0,
name: value1,
organization: organization1,
...,
},
1: {
id: 1,
name: value1,
organization: organization1,
...,
},
2: {
id: 2,
name: value2,
organization: organization2,
...,
},
...
}
I want to filter by id and remove the Object which id matches the id I receive from the store. What I tried so far:
const filteredObject = Object.keys(this.tokensData).map((token) => {
if (this.$store.state.id !== this.tokensData[token].id) {
return this.tokensData[token];
}
});
This replaces the Object with undefined - which would work for my purposes but is obviously not ideal.
Any help is much appreciated!
Try to use Object.entries and then Object.fromEntries() to create an object from a list of key-value pairs:
let store = [0 , 1];
const result = Object.entries(tokensData).filter(([k, v]) => !store.some(s => s == v.id));
console.log(Object.fromEntries(result));
An example:
let tokensData = {
O: {
id: 0,
name: '',
organization: '',
},
1: {
id: 1,
name: '',
organization: '',
},
2: {
id: 2,
name: '',
organization: '',
}
}
let store = [0 , 1];
const result = Object.entries(tokensData).filter(([k, v]) => !store.some(s => s == v.id));
console.log(Object.fromEntries(result));
You can do this by using Object.entries and Object.fromEntries:
const filteredObject = Object.fromEntries(
Object.entries(this.tokensData).filter(
([key, value]) => value.id !== this.$store.state.id
)
)
This can be done by cloning the object and removing the object at the ID:
const removeObjectByID = (obj, id) => {
// first create a copy of the object
const copy = JSON.parse(JSON.stringify(obj))
// next, delete the one entry you don't want
delete copy[id]
// finally, return the new object
return copy
}
// Test
const data = {a:1, b: 2, c: 3}
console.log(removeObjectByID(data, 'b')) // { a: 1, c: 3 }
The problem with undefined is caused by using this in your arrow function. Javascript scoping gives a different meaning to this if the function is a arrow function.
Furthermore I suggest to use filter.
.map is used to transform A -> B.
.filter should be used to filter out objects.
Now if we combine that this would become something like this.
function filterById(token) {
return this.$store.state.id !== this.tokensData[token].id;
}
function getTokenData(token) {
return this.tokensData[token]
}
const token = Object.keys(this.tokensData)
.filter(filterById)
.map(getTokenData);
});
Please note that I'm not using arrow functions. Arrow function can't refer to this due to the way javascript handles scoping.
An alternative approach could be to reference this into a variable, so your arrow function can access the variable.
const self = this;
const token = Object.keys(this.tokensData)
.filter(token => self.$store.state.id !== self.tokensData[token].id)
.map(token => self.tokensData[token]);
Too make it even nicer you could utilize Object.entries. This will return an array of key and value, which you can destructure using es6 syntax as following [key, value].
const self = this;
const token = Object.entries(this.tokensData)
.filter(([key, value]) => self.$store.state.id !== value.id)
.map(([key, value]) => value);

How to create object from another without undefined properties [duplicate]

This question already has answers here:
Remove blank attributes from an Object in Javascript
(53 answers)
Closed 2 years ago.
Is there a better way to accomplish this in Javascript?
const data: any = {};
if (values.email) data.email = values.email;
if (values.password) data.password = values.password;
if (values.username) data.username = values.username;
I don't want the data object to have the properties for the undefined or falsy values.
You could put the potential properties in an array and then .filter() out any which values[prop] has a fasly value for. Then you can .map() each key to an array of [key, value] pairs, and use Object.fromEntries() to build the data object for you:
const values = {
email: 'abc',
username: 'xyz'
};
const props = ['email', 'password', 'username'];
const data = Object.fromEntries(
props.filter(prop => values[prop]).map(prop => [prop, values[prop]])
);
console.log(data);
If you can't support Object.fromEntries(), you could consider using Object.assign(), and then mapping to an array of objects which you then spread into Object.assign():
const values = {
email: 'abc',
username: 'xyz'
};
const props = ['email', 'password', 'username'];
const data = Object.assign({},
...props.filter(prop => values[prop]).map(prop => ({[prop]: values[prop]}))
);
console.log(data);
You can do something along these lines:
const obj1 = {
prop1: true,
prop2: false,
prop3: 4,
}
const obj2 = Object.entries(obj1).reduce((result, [key, value]) => {
if (value) {
result[key] = value
}
return result
}, {})
console.log(obj2)
This simple function will do it, if you want to copy over all properties that don't have a false-y value. If you only want a fixed list, look at the answer from Nick Parsons.
const copyNonNil = (obj) =>
Object .fromEntries (Object .entries (obj) .filter(([k, v]) => v))
const values = {
email: 'foo#bar.baz',
username: 'foobar',
password: '',
another: false
}
console .log (copyNonNil (values))
It's easy to shim Object .fromEntries if you don't have it available. See the answer from theDude, which uses a reduce that serves in the same role.
You could use Object.entries to iterate over the properties of values and perform the true/false check in that to make this more flexible:
for(const [key, value] of Object.entries(values)){
if(values[key]) data[key] = value;
}
This would give you only the truthy values in data for a values object of any size.

Map an object array with unknown length and unknown key names

Here are some object arrays:
1. [{id:'1', code:'somecode', desc:'this is the description'}, {...}, {...}]
2. [{fname:'name', lname:'last name', address:'my address', email:'my#email.com'}, {...}, {...}]
What I need to do is create a function where I pass an array and map their object keys to generic keys so they become like this:
1. [{key1:'1', key2:'somecode', key3:'this is the description'}, {...}, {...}]
2. [{key1:'name', key2:'last name', key3:'my address', key4:'my#email.com'}, {...}, {...}]
When I do this
let keys: string[] = Object.keys(this.options[0])
this.replacedItems = this.options.map(item => {
return{
key1: item[keys[0]],
key2: item[keys[1]],
key3: item[keys[2]],
}
});
it works fine, but since the object's properties number is not fixed, I tried this
let keys: string[] = Object.keys(this.options[0])
this.replacedItems = this.options.map(item => {
let i=0;
keys.forEach(key=>{
let newKey = 'key'+i;
i++
return { newKey: item[key] }
});
});
which rerurns an array of undefined...
What am I doing wrong?
Take the second parameter of .map to get the current index you're iterating over, and concatenate it with 'key'. You can also use Object.values instead of Object.keys to get the values immediately (since you're not actually using the original keys):
const options = [{id:'1', code:'somecode', desc:'this is the description'}];
const replacedItems = options.map(obj => Object.fromEntries(
Object.values(obj).map((val, i) => ['key' + (i + 1), val])
));
console.log(replacedItems);

Is there any way to simplify this two-line expression? (destructuring and regrouping)

Here's the expression:
const { property1, property2 } = someObject;
return { property1, property2 };
Basically, I want to extract several properties from one object and then create a new object just with those properties. It feels as if it could be expressed in a simpler way, but I'm out of ideas.
You could always use the lodash pick method (Lodash Pick Documentation) this will give a one-liner.
const obj = { property1: "property1", property2: "property2", property3: "property3"};
function testPick(someObject) {
return _.pick(someObject, ["property1", "property2"]);
}
console.log("testPick: ", testPick(obj));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You could write a higher order function to extract an object with specific properties:
const pick = (...p) => o => p.reduce((a, k) => Object.assign(a, { [k]: o[k] }), {})
// usage
const someObject = {
property1: 'property1',
property2: 'property2',
property3: 'property3'
}
// function that extracts these two properties from an object
const pair = pick('property1', 'property2')
console.log(someObject)
console.log(pair(someObject))
You could also define pick() this way, though the inner function would use stack memory proportional to the amount of keys extracted:
const pick = (...p) => o => Object.assign({}, ...p.map(k => ({ [k]: o[k] })))
// usage
const someObject = {
property1: 'property1',
property2: 'property2',
property3: 'property3'
}
// function that extracts these two properties from an object
const pair = pick('property1', 'property2')
console.log(someObject)
console.log(pair(someObject))

Convert single element in array of objects

I am trying to convert all elements of a given key in a list of objects. The specific change is to convert an generic object to a Long object with the same values.
Input:
[obj1, obj2, obj3, ...]
Where each object looks like:
{
key1: value1, // value1 is an object
key2: value2
}
So the goal here is to get a very similar array, but where key1 is transformed from just object to a long object, in my example incremented by one.
Output:
[obj1, obj2, obj3]
Where each object is:
{
key1: value1, // value1 is now a Long object
key2: value2
}
I tried with mapping over the array and then spreading over the objects entries, but did not succeed.
Any help would be highly appreciated.
You don't really need to map here, unless you want to create one more new array. Simple loop would be enough, just update key1 or each object:
[obj1, obj2, obj3].forEach(obj => {
obj.key1 = obj.key1 + 1
})
Since the array holds object - any modification to object reflect the actual input being passed. You need to make a copy of your object and perform the logic.
Your logic,
var input = [obj1, obj2, obj3];
var output = input.map(function(obj){
obj.key1 = obj.key1 + 1;
return obj;
});
console.log(input[0]===output[0]); //true
Solution:
var output = input.map(function(obj){
var _objCopy = Object.assign({}, obj);
_objCopy.key1 = _objCopy.key1 + 1;
return _objCopy;
});
console.log(input[0]===output[0]); //false
how about dynamically first key of any object es6 way
const myObject0 = {
'foo1': { name: 'myNam1' },
'foo2': { name: 'myNam2' }
};
const myObject1 = {
'yo': { name: 'myNam1' },
'boh': { name: 'myNam2' }
};
[myObject0, myObject1].forEach(obj => {
let getFirstKey = Object.keys(obj)[0];
getFirstKey = getFirstKey + 1;
console.log(getFirstKey);
});
Thanks for all the answers guys.
I ended up with the following:
Given the input
[
{
key1: value1, // value1 is an object
key2: value2
},
{
key1: value1, // value1 is also an object
key2: value2
}
]
Solution:
I ended up with,
const testvar = [obj1, obj2, obj3].map((obj) => {
return {
...obj,
key1: new Long.fromValue(obj.key1)
}
});
console.log(testvar);
Which gives the output
[
{
key1: value1, // value1 is now a Long object
key2: value2
},
{
key1: value1, // value1 is also now a Long object
key2: value2
}
]

Categories