Spread syntax not working as expected with array of objects - javascript

I have a react component where I am trying to spread objects into the state in the constructor.
constructor() {
super()
const shapesArray = [1, 2, 3]
let renderStates = shapesArray.map((el, i) => {
return {['shape'+i]: 'black'}
})
this.state = { ...renderStates }
console.log(this.state)
}
I want to access the colors by doing this.state.shape0,
but when I console log this.state, I get this:
instead of Object {shape0: "black", shape1: "black", shape2: "black"}.
What am I doing wrong here?

That is because you are spreading an Array into an Object. Arrays are actually objects with (usually) sequential integral strings as their keys. These keys are the indices of the array.
As shown below, map takes an array and produces another array
const shapesArray = [1, 2, 3];
const renderStates = shapesArray.map((el, i) => {
return {
['shape' + i]: 'black'
}
});
console.log(renderStates);
When spreading into an Object, the value of each own enumerable property in the source Object is added to the target under its respective key. Since the keys of an array are its indices you end up with an Object with a property for each element of the Array. The name of each property is its index in the array.
To achieve what you want, you can use Array.prototype.reduce to build an object from the array with the names created in the mapping process.
const shapesArray = [1, 2, 3];
const renderStates = shapesArray
.map((el, i) => {
return {
['shape' + i]: 'black'
}
})
.reduce((o, element) => {
Object.keys(element).forEach(key => o[key] = element[key]);
return o;
}, {});
console.log(renderStates);
Of course this itself can be written more elegantly by spreading the object inside of reduce.
const shapesArray = [1, 2, 3];
const renderStates = shapesArray
.map((el, i) => {
return {
['shape' + i]: 'black'
}
})
.reduce((o, element) => ({...o, ...element}), {});
console.log(renderStates);

As an optimization to aluan-haddad's answer, reduce can handle the logic that was in map
const shapesArray = [1, 2, 3];
const renderStates = shapesArray
.reduce((acc, _, i) => ({
...acc,
['shape' + i]: 'black',
}), {});
console.log(renderStates);

renderStates is an array which has integer properties starting from 0 or the array indices if you want to be specific, so {...renderStates} will take each index, and create a mapping from this index to the value corresponding to that index, to achieve what you are looking for, you need to reduce your renderStates array to an object like so
let renderStates = shapesArray.map((el, i) => {
return {['shape'+i]: 'black'}
}).reduce((resultObj, currentShape,index) => resultObj['shape'+index] = currentShape['shape'+index]), { });
renderStates is now an object, and you can spread it and it will produce the result you want

No need to iterate twice over array. Use reduce:
const shapesArray = [1, 2, 3];
const renderStates = shapesArray.reduce((accumulator, i) => {
accumulator['shape' + i] = 'black';
return accumulator;
}, {});
console.log(renderStates);

Related

How to iterate through map and push keys with similar values into the same array?

I've got javascript map with structure like this:
let map = {3 => 1,
15 => 2,
0 => 2,
8 => 3,
9 => 3}
I need to receive the arrays of keys, and keys with similar values should be in the same array.
[[3], [15,0],[8,9]]
This is what I've tried:
let answers = [];
let currentVal = 1;
map.forEach((value, key)=>{
let subArr = [];
if(value === currentVal){
subArr.push(key);
answers.push(subArr);
currentVal++;
}
});
return answers;
And it returns [[3], [15], [8]]
I assume your map is an object, not map for readability reasons, but if u are using Map there you can change methods to get elements but main idea you can see below:
const data = {
3: 1,
15: 2,
0: 2,
8: 3,
9: 3
};
const customData = Object.entries(data).reduce((acc, [key, value]) => {
if (value in acc) {
acc[value].push(key);
} else {
acc[value] = [key];
}
return acc;
}, {});
const finalArray = Object.values(customData);
console.log(finalArray);
Edit with Map() example:
const data = new Map([
[3, 1],
[15, 2],
[0, 2],
[8, 3],
[9, 3]
]);
const customData = Array.from(data).reduce((acc, [key, value]) => {
if (value in acc) {
acc[value].push(key);
} else {
acc[value] = [key];
}
return acc;
}, {});
const finalArray = Object.values(customData);
console.log(finalArray);
You could use Object.entries and destructuring key, value but I change their place as value, key to match the data structure map provided by OP :
let map = { 3: 1, 15: 2, 0: 2, 8: 3, 9: 3 };
const arr = [];
for (const [value, key] of Object.entries(map)) {
if (arr[key]) {
arr[key].push(value);
} else {
arr[key] = [value];
}
}
console.log(arr.filter(Boolean)); // [[3], [15,0],[8,9]]
NOTE arr.filter(Boolean) to remove falsy (empty) values from array since index 0 have no array inside of it!
Your map is not a valid one. Hence, I am creating it by using Map instance. You can have a look in this official documentation of Map Object.
Live Demo :
// Creating a map object by using Map instance
const map = new Map();
// Setting the key, values in map object.
map.set(3, 1)
map.set(15, 2)
map.set(0, 2)
map.set(8, 3)
map.set(9, 3)
// Declaring an empty array which will store the result.
const arr = [];
// Iterate over a map object and assign the keys of map object into an 'arr'.
for (const [key, value] of map) {
(arr[value]) ? arr[value].push(key) : arr[value] = [key];
}
// Output
console.log(arr.filter(Boolean));

Get all Indexes of Objects in Array - from Array in Object

i´m struggling with a Array in a Object stored in a Array with Objects from which I want return all Indicies.
Function to generate Object looks like this:
const addArray = function(a, b) {
const object = {
name: a,
rooms: b
};
testArray.push(object);
};
What I want to achieve is to cycle through the "testArray" and return every Index from the Object where the Array Rooms contains "Office" for example.
I´ve already tried to use a function like this but I don´t seem to be able to get the right Syntax for the Array in the Object:
function getAllIndexes(arr, val) {
var indexes = [], i = -1;
while ((i = arr.rooms.indexOf(val, i+1)) != -1){
indexes.push(i);
}
return indexes;
};
Thanks in advance!
Edit:
Additional Informations to Data:
A Object with data filled would look like this:
const device = {
name: "TV",
rooms: ["Living Room", "Bedroom"]
};
After generating Objects like this I push them to an array witch only contains this objects (see function addArray)
You can use Array.flatMap() to map each value of the array at matches val to it's index, and the rest to empty array, which will be removed by the flatMap:
const getAllIndexes =(arr, val) => arr.flatMap((v, i) => v === val ? i : [])
const arr = [1, 2, 3, 1, 2, 1, 1, 2]
const result = getAllIndexes(arr, 1)
console.log(result)
Using your array of objects, you'll need to compare a value, or check if an object meets some condition. It's better in this case to replace val with a predicate function:
const getAllIndexes =(arr, pred) => arr.flatMap((v, i) => pred(v) ? i : [])
const arr = [{ rooms: [1, 2, 3] }, { rooms: [2, 1, 1] }, { rooms: [3, 2, 2] }, { rooms: [1, 2, 1] }]
const result = getAllIndexes(arr, o => o.rooms.includes(1))
console.log(result)
Try using Array.prototype.map and Array.prototype.filter
function getAllIndexes(arr, val) {
return arr.map(i=> {
let room = i.rooms;
return room.indexOf(val);
}).filter(a=>{
a != -1;
});
};
You could destructure rooms from the device and get the index, if the wanted value is found.
const
room = 'Office',
indices = array.flatMap(({ rooms }, i) => rooms.includes(room) ? i : []);
The above code features a former solution from me with hacking Array#flatMap.

Sorting a JS array based on an array with new indices

I can't seem to find a neat solution to this fairly simple problem. I have an array of objects likes this:
let items = [{/* */}, {/* */}, {/* */}]
Additionally, i have an array containing new array indices that i want to apply to the above items:
const newIndices = [2,0,1]
Meaning items[0]'s new index is 2, items[1]'s new index is 0, etc...
Right now i am using forEach, but this method requires a temporary array:
const tempItems = []
newIndices.forEach((newIndex, oldIndex) => {
tempItems[newIndex] = items[oldIndex]
})
items = tempItems
I'm almost certain there is a neat one liner for this problem. I've also tried mapping, but without luck.
You could map the new index/value pairs with objects and assign to an array.
const
items = ['foo', 'bar', 'baz'],
newIndices = [2, 0, 1],
result = Object.assign([], ...items.map((v, i) => ({ [newIndices[i]]: v })));
console.log(result); // bar baz foo
working code
let items = ['foo', 'bar', 'baz']
const newIndices = [2, 0, 1]
const result = items.map((item, index) => items[newIndices.indexOf(index)])
console.log(result)
One way is to use Array.prototype.reduce():
let items = ['foo', 'bar', 'baz']
const newIndices = [2,0,1]
const result = newIndices.reduce((acc, indice, i) => {
acc[indice] = items[i]
return acc
}, [])
console.log(result)
let items = ['foo', 'bar', 'baz']
const newIndices = [2, 0, 1]
items = newIndices.map((index) => newIndices[index]).map((index) => items[index])
console.log(items)

How to change from array of values to array of objects?

I got data like this:
{"my_data":{"labels":[1,2,3,...], "idx":["idx1", "idx2", ...]}}
But I need it like this:
["my_data":{"labels":1, "idx": "idx1"},{"labels":2, "idx": "idx2"},... ]
I tried to loop like this to change the format:
var arr = [];
for (let [key, value] of Object.entries(my_data)) {
arr.push({[key]:value});
}
console.log(key, value);
But the result is a format I can not use with ag-grid
labels: [1,2,...]
idx: [idx1, idx2,...]
One way you can approach this is by using the map method like in the following example:
const temp = { my_data: { labels: [1, 2, 3], idx: ["idx1", "idx2", "idx3"] } };
const result = temp.my_data.labels.map((label, index) => {
const idx = temp.my_data.idx[index];
return {
label,
idx
};
});
console.log(result)
This way, you're mapping through labels then extracting the value for idx based on the index of the label.

Pick the array by property name from another array

I have an array like that
I want to pick the array item by property name, I am using lodash for that:
const result = _.map(this.thing, _.property('groups')).filter(x => x !== undefined);
But I am getting array of arrays as result
What I need is just single selected property array.
Any idea how to achieve that?
Try this>>>
var a = [{"p1":[3,4]},{"p2":[6,7]}];
function getArr(arr,key){
var res = [];
for(var v of arr){
if(v[key]!=undefined){
res = v[key];break;
}
};
return res;
}
console.log(getArr(a,"p1"));
If you can use ES6/ES7, you can rely on Object.keys and Object.values to access to the key (that is the property name) and the value (the array you want to get):
var arr = [
{ groups: [1, 2 ] },
{ category: [1, 2, 3 ] },
{ subCategory: [1, 2, 3, 4 ] }
];
function pickArray(propertyName) {
var element = arr.find(el => Object.keys(el)[0] === propertyName)
return element ? Object.values(element)[0] : null;
}
var res = pickArray('category');
console.log(res);
const output
= (Array.from(arr, (obj) => obj['product'], 'product')
.filter(x => typeof x !== 'undefined'))[0];
Try this:
const arr = [ {'groups': ['item1','item2']},
{'categories':['x','y']}
]
var ouptut= arr.find(item=> {
return item[Object.keys(item).find(key=>key === 'groups')]
})
console.log(ouptut)

Categories