I have this:
[ { list:
[ [Object],
[Object] ] },
{ head:
[ [Object],
[Object] ] }
]
And want to turn it into this:
{ list:
[ [Object],
[Object] ],
head: [ [Object],
[Object] ]
}
So an array of objects into an object. It would be great to achieve this with lodash.
I think an even shorter solution would be:
Object.assign({}, ...array)
I know you asked for lodash, but it doesn't seem like you even need this way. Unless you want to use _.extend.
_.reduce(array, function(memo, current) { return _.assign(memo, current) }, {})
Here's a shorter version:
_.transform(array, _.ary(_.extend, 2), {});
The transform() function is like reduce(), except it's not expecting you to return anything. Since extend() is altering it's first argument, we can just pass it straight to transform(). It's wrapped in ary() to make sure it only gets 2 arguments passed to it.
To build on #rcsole's great answer, this works well:
states = [{
state: "NY",
name: "New York",
}, {
state: "AZ",
name: "Arizona",
}]
statesObj = Object.assign({}, ...states.map(state => {
return { [state.state]: state.name }
}))
Result:
{
AZ: "Arizona",
NY: "New York",
}
What's going on here?
Let's break this up into multiple pieces:
// Step 1: Transform from [{state: "Foo", name: "Bar"}, ...] to [{Foo: "Bar"}, ...]
mappedStates = states.map(state => { return { [state.state]: state.name } })
// Step 2: Merge all the objects in that array into a single new object
statesObj = Object.assign({}, ...mappedStates)
Step 1 uses map to iterate over every item in the array (every state object). map executes a function for every state object and returns a new object with the state as the key and the name as the value. We need to surround state.state in brackets because it's a dynamic value in an object literal.
Step 2 uses Object.assign to merge all the new state objects in the mappedStates array into a new object (the first parameter, {}).
What are the three dots ... for? That's the spread operator. It takes every element in the mappedStates array and turns them into direct arguments of the Object.assign method.
This example makes it clear:
Object.assign({}, ...mappedStates)
is the same as
Object.assign({}, {AZ: "Arizona"}, {NY: "New York"})
That's it!
Use fromPairs on lodash 4. https://lodash.com/docs#fromPairs
_.fromPairs([['fred', 30], ['barney', 40]]);
Related
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);
I have a nested array of objects. How to get nested array item where object property id is "random" and set name to "you won it"
The structure looks like this:
[
{
itemsBundle [
{
id: 'selection'
name: 'failed'
}
{
id: 'random'
name:'win'
}
]
basketId: 'item'
basketName
tags[{}{}]
}
{}
{}
]
I need somehow get the object inside the main array where nested itemsBundle array of objects containes object with id 'random' and then for that itemBundle's single object where id is 'random' set name from win to you won it. I thought about using nested map() with filter() or nested loops but not sure which option will be the best and how can this results be achieved with less complicated way. The only 3rd party library that I am using is lodash.
Working example with flatMap and find. BUT it is mutating the main content, you have to clone deep you list first.
Improve your question next time :)
const aa = [
{
itemsBundle: [
{
id: "selection",
name: "failed"
},
{
id: "random",
name: "win"
}
],
basketId: "item",
basketName: "",
tags: [{}, {}]
},
{},
{}
];
const items = aa.flatMap(a => a.itemsBundle || [])
const itemFound = items.find(item => item.id === 'random')
itemFound.name = 'you win'
console.log(aa[0].itemsBundle[1])
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
I'm having a similar need to a previous post on this topic: needing to order and sort an object without losing the keys. However, I have an object of objects:
var o = {
123: { field: "science", name: "zed" },
234: { field: "tech", name: "sara" },
672: { field: "arts", name: "jon" }
}
_.fromPairs(_.sortBy(_.toPairs(o),function(a){ return a[1] }).reverse())
The above uses the lodash solution mentioned the other topic - I'm however not getting any consistency of results (tho am retaining the key!)
Any help would be appreciated.
I’m not clear on what value you intended to sort against above, since objects are incomparable. Let’s say you wanted to sort them by the name field, though — you would find that it still didn’t work.
First some background:
Historically, object properties were considered unordered in ES. The sequence that keys would be enumerated (e.g. by for ... in or Object.keys()) was implementation-specific.
However enumeration order became a specified behavior in ES2015.
That enumeration order codifies what most engines were already doing:
Keys which are integers are enumerated first, from lowest to highest.
String keys are enumerated in the order of assignment.
Symbol keys are enumerated in the order of assignment.
If you need to use integers as ordered keys, you’ll require a different data structure. Map is appropriate:
const o = {
123: { field: "science", name: "zed" },
234: { field: "tech", name: "sara" },
672: { field: "arts", name: "jon" }
}
const { compare } = new Intl.Collator();
const res = new Map(Object
.entries(o)
.sort(([ , a ], [ , b ]) => compare(a.name, b.name))
);
console.log([ ...res ]);
If you need to worry about old IE, an array of key-value entries would also suffice.
I have an issue where an API call I'm using is sending objects with one property that contains a single array value (keys property in response below). Unfortunately I cannot use this format as I must abide by Nested arrays in order to use the outputted values in a separate application like so [[value1, value2, value3, value4],[value1, value2, value3, value4]]. I plan on asking a separate question to tackle the nested array section unless someone thinks it is an easy fix (I believe I should use .map to convert the object).
Here is the format of my objects (from console.log(searchQueries)):
[ { keys: [ 'hammer' ],
clicks: 1369,
impressions: 3151,
ctr: 0.4344652491272612,
position: 1.004443033957474 },
{ keys: [ 'woodmaking' ],
clicks: 207,
impressions: 6324,
ctr: 0.03273244781783681,
position: 4.35831752055661 },
{ keys: [ 'house trends' ],
clicks: 1,
impressions: 3,
ctr: 0.3333333333333333,
position: 4.666666666666666 },
{ keys: [ 'housing' ],
clicks: 1,
impressions: 36,
ctr: 0.027777777777777776,
position: 6.472222222222222 } ]
byProperty
Above response is passed from the following for-in loop the result of this API response array being nested in an object originally:
for (var prop in res){
searchQueries = res[prop];
console.log(searchQueries);
}
Would the JSON.stringify method or .toString('keys') achieve what I'm looking for?
If you want to turn keys from an array into a string, you just need to iterate over your array and make the change:
searchQueries.forEach(function (obj) { obj.keys = obj.keys[0] })
answer=Object.values(searchQueries).map(el=>{el.keys=el.keys[0];return Object.values(el)});
console.log(searchQueries);
https://jsbin.com/biyazunafu/1/edit?console
Loop over the main array, turn the Objects (el) keys array into a string, than turn the whole object into its values Array. However, Object.values is experimental, so may dont use this on the users side, instead use this code transpiled to ES5 :
answer=[];
for(key in searchQueries){
answer.push(searchQueries[key]);
}
answer=answer.map(function(el){
el.keys=el.keys[0];
var newel=[];
for(key in el){
newel.push(el[key]);
}
return newel;
});
1st Get the keys values
var keys = searchQueries.map(function(e){return e.keys;});
This will output :
[["hammer"], ["woodmaking"], ["house trends"], ["housing"]]
2nd: Concat the resulting array
var values = keys.join(',').split(',');
The result :
["hammer", "woodmaking", "house trends", "housing"]