create an array of objects based on another array of objects programmatically - javascript

here is the array of objects I have:
var sentences = [
{ id: "1-1", layer: ["A"] },
{ id: "1-2", layer: ["B"] },
{ id: "1-3", layer: ["C"] },
{ id: "2-1", layer: ["D"] },
{ id: "2-2", layer: ["E"] },
{ id: "2-3", layer: ["F"] },
{ id: "3-1", layer: ["G"] },
{ id: "3-2", layer: ["H"] },
{ id: "3-3", layer: ["I"] },
];
the first number in id is the slide number and the second one is for the sentence on that slide. for example id: 3-2 is slide 3 and sentence 2.
so we have 3 slides here...
Now the problem is How can I programmatically fill the slides array like this:
var slides = [
{ slide_id: "1", slide_layer: ["A", "B", "C"] },
{ slide_id: "2", slide_layer: ["D", "E", "F"] },
{ slide_id: "3", slide_layer: ["G", "H", "I"] },
]
I'm still researching but I can't find a proper way...

You can use reduce method to group slides by id using object as accumulator, and then you can get array of values from that object with Object.values method.
var sentences = [{"id":"1-1","layer":["A"]},{"id":"1-2","layer":["B"]},{"id":"1-3","layer":["C"]},{"id":"2-1","layer":["D"]},{"id":"2-2","layer":["E"]},{"id":"2-3","layer":["F"]},{"id":"3-1","layer":["G"]},{"id":"3-2","layer":["H"]},{"id":"3-3","layer":["I"]}]
const result = Object.values(sentences.reduce((r, {id, layer}) => {
let [a, b] = id.split('-');
if(!r[a]) r[a] = { slide_id: a, slide_layer: [] }
r[a].slide_layer[b - 1] = layer[0]
return r;
}, {}))
console.log(result)

We can use Array.prototype.reduce to accumulate the slides from the sentences array. We need to test if the slide object is already there or not in the accumulator array using Array.prototype.findIndex() and slide_id.
If it is present (idx is >= 0) we need to add the layer to the slide_layer array property of the slide object.
var sentences = [
{ id: "1-1", layer: ["A"] },
{ id: "1-2", layer: ["B"] },
{ id: "1-3", layer: ["C"] },
{ id: "2-1", layer: ["D"] },
{ id: "2-2", layer: ["E"] },
{ id: "2-3", layer: ["F"] },
{ id: "3-1", layer: ["G"] },
{ id: "3-2", layer: ["H"] },
{ id: "3-3", layer: ["I"] },
];
var slides = sentences.reduce((acc, ele) => {
let idx = acc.findIndex(obj => obj.slide_id === ele.id.split("-")[0]);
if(idx >= 0){
let obj = acc[idx];
obj.slide_layer.push(ele.layer[0]);
}else{
acc.push({slide_id: ele.id.split("-")[0], slide_layer : [ele.layer[0]]});
}
return acc;
}, []);
console.log(slides);

Here is the solution with easy to read code.
let slides = [];
for(let data of sentences) {
let slideId = parseInt(data.id.split("-")[0]);
let slideIndex = slideId-1;
if (!slides[slideIndex]) slides[slideIndex] = {"slide_id": slideId, "slide_layer": []}
slides[slideIndex].slide_layer.push(data.layer[0]);
}
console.log("slides", slides);

The array reduce() method in JavaScript is used to reduce the array to a single value and executes a provided function for each value of the array (from left-to-right) and the return value of the function is stored in an accumulator.
const output = Object.values(sentences.reduce((k, {id, layer}) => {
let [frst, sec] = id.split('-');
if(!k[frst])
k[frst] = { slide_id: frst, slide_layer: [] }
k[frst].slide_layer[sec - 1] = layer[0]
return k;
}, {}))

Related

ES6 map.has is not a function when called in a reducer

I want to return an array of objects without any duplicate ids. If there are any, then take the first one we see. So, we should NOT see {id: "2", value: '10'}. Instead, the value should be "Italian". I have this code below, but I am getting an map.has is not a function error.
const arr1 = [{
id: "1",
value: "English"
},
{
id: "2",
value: "Italian"
}
];
const arr2 = [{
id: "2",
value: '10'
},
{
id: "3",
value: "German"
}
];
const concatArr = arr1.concat(arr2);
const mergedArr = [...concatArr.reduce((map, obj) => map.has(obj.id) ? "" : map.set(obj.id, obj), new Map()).values()];
console.log(mergedArr);
You need to always return a map not an empty string when the thing is already in the map.
const arr1 = [{
id: "1",
value: "English"
},
{
id: "2",
value: "Italian"
}
];
const arr2 = [{
id: "2",
value: '10'
},
{
id: "3",
value: "German"
}
];
const concatArr = arr1.concat(arr2);
const mergedArr = [...concatArr.reduce((map, obj) => map.has(obj.id) ? map : map.set(obj.id, obj), new Map()).values()];
console.log(mergedArr);
You can use array#reduce to uniquely identify each object with unique id in an object accumulator and then extract all values from this object using Object.values().
const arr1 = [{ id: "1", value: "English" }, { id: "2", value: "Italian" } ],
arr2 = [{ id: "2", value: '10' }, { id: "3", value: "German" } ],
result = Object.values(arr1.concat(arr2).reduce((r, o) => {
r[o.id] = r[o.id] || o;
return r;
},{}));
console.log(result);

Count and update duplicate entries from array

I have an array with duplicated entries.
I'm looking for:
removing all duplicate occurrences
And increment the val propertie off an element if we find an occurrence
here is an exemple of what I have:
const arr = [
{"id":"46","name":"Productivity","val":1},
{"id":"1","name":"test","val":1},
{"id":"43","name":"Health and Fitness","val":1},
{"id":"46","name":"Productivity","val":1},
{"id":"1","name":"test","val":1},
{"id":"46","name":"Productivity","val":1}
]
// Wanted result
const result = [
{"id":"46","name":"Productivity","val":3},
{"id":"43","name":"Health and Fitness","val":1},
{"id":"1","name":"test","val":2}
]
Here is a JsFiddle
You can easily achieve this using reduce and object destructuring.
const arr = [
{ id: "46", name: "Productivity", val: 1 },
{ id: "1", name: "test", val: 1 },
{ id: "43", name: "Health and Fitness", val: 1 },
{ id: "46", name: "Productivity", val: 1 },
{ id: "1", name: "test", val: 1 },
{ id: "46", name: "Productivity", val: 1 },
];
const result = arr.reduce((acc, curr) => {
const { id, name, val } = curr;
const isPresent = acc.find((el) => el.id === id);
if (!isPresent) acc = [...acc, { id, name, val }];
else isPresent.val = isPresent.val + val;
return acc;
}, []);
console.log(result);

Merge nested relations to an Object

I have an array of objects that also contains array like this:
let array = [{"a": ["b", "c"]} , {"b":[ "d" ]}, {"e":[ "f" ]}]
I need to display it as Tree View and I need to get the output like below:
[
{
id : "a",
children: [
{
id: "b",
children: [{id: "d", children: [] }]
},
{
id: "c",
children: []
}
]
},
{
id: "e",
Children: [
{
id: "f",
children: []
}
]
}
]
I tried to do it by creating an array of all the parents [a, b, e], and depth first search through the entire array but I could not get the correct output.
Can someone help? Thanks in advance.
Try this recursive approach:
var array = [{ "a": ["b", "c"] }, { "b": ["d"] }, { "e": ["f"] }]
let object = {}
array.map(itm => Object.keys(itm).map(name => object[name] = itm[name]))
const treeView = (key, val) => {
delete object[key]
return {
id: key,
children: getChildrens(val)
}
}
const getChildrens = (arr) => {
let childrens = []
return arr.map(itm => {
if (object[itm]) {
return treeView(itm, object[itm])
}
return { id: itm, children: [] }
})
}
let res = Object.keys(object).map((val, idx) => {
if (object[val])
return treeView(val, object[val])
}).filter(e=>e)
console.log(res)

Map over two arrays to see if one property matches, then push specific info into the first array

New to javascript and trying to learn! I am trying to map through two array of objects, and if a certain property matches, pull in specific information into the first array.
let result;
let arrNames = [{
id: 10
name: "A"
}, {
id: 11,
name: "B"
}, {
id: 12,
name: "C"
}, }, {
id: 13,
name: "A"
}, {
id: 14,
name: "B"
}]
let arrInfo = [{
name: "A",
info: "AAA"
}, {
name: "B",
info: "BBB"
}, {
name: "C",
info: "CCC"
}]
If arrNames.name == arrInfo.name, I would like push info into the names array.
Desired result:
let arrNames = [{
id: 10
name: "A",
info: "AAA"
}, {
id: 11,
name: "B",
info: "BBB"
}, {
id: 12,
name: "C",
info: "CCC"
}, }, {
id: 13,
name: "A",
info: "AAA"
}, {
id: 14,
name: "B",
info: "BBB"
}]
What I've tried:
const res = arrInfo.map((el, index) => {
if(el.name == arrNames[index].name)
arrNames.push(el.info)
}
^ This obviously doesn't work -- but I'm wondering if extend or push would be appropriate here.
Thanks in advance for your help (apologies that this is probably a dupe).
Convert arrInfo to a Map, with the name as the key. Now map arrNames and add the info you get from arrInfoMap using the name. Use object spread to combine both objects:
const arrNames = [{"id":10,"name":"A"},{"id":11,"name":"B"},{"id":12,"name":"C"},{"id":13,"name":"A"},{"id":14,"name":"B"}]
const arrInfo = [{"name":"A","info":"AAA"},{"name":"B","info":"BBB"},{"name":"C","info":"CCC"}]
const arrInfoMap = new Map(arrInfo.map(o => [o.name, o]))
const result = arrNames.map(o => ({ ...o, ...arrInfoMap.get(o.name) }))
console.log(result)
You can do something like this:
let arrNames = [
{
id: 10,
name: 'A'
},
{
id: 11,
name: 'B'
},
{
id: 12,
name: 'C'
},
{
id: 13,
name: 'A'
},
{
id: 14,
name: 'B'
}
];
let arrInfo = [
{
name: 'A',
info: 'AAA'
},
{
name: 'B',
info: 'BBB'
},
{
name: 'C',
info: 'CCC'
}
];
// do this
const result = arrNames.map((item) => {
const newItem = item; // here we define a new object that is the same as your object that is currently looped up to in your arrNames array
// loop your second array over this currently looped to object, seeing if the name matches
arrInfo.forEach((item2) => {
if (item.name === item2.name) {
newItem.info = item2.info; // if they do set a new property for your new object called info as the info from item 2 of this arrInfo array
}
});
// return this new object whether or not there was a match for the name property
return newItem;
});
console.log(result);
So the thing with your map method is that you need to remember to return something at the end of your callback function. You are simply pushing to an array, which is like using .map as a forEach. Map makes one array into another array of the same length. Here you are trying to make a new array where the array element being looped over will have an extra info property should it match your second array arrInfo's name.
So you what you can do is a forEach inside your map to check if they match, if so add a new property to your arrayNames element and return that as the new element for your newly created array. Hope that helped, please ask for clarifications if you need in the comments.

How to add new attribute that need to be calculated recursively in JavaScript

I want to dynamically add new attribute to all of the items in an array using map. But while adding it to each item, the value of this attribute is the sum of all previous items newly added attribute
Consider this example :
let persons = [{
"name": "A",
"salary": 2
}, {
"name": "B",
"salary": 5
},{
"name":"C",
"salary":12
}];
I want to return :
[{
"name": "A",
"salary": 2,
"sumSalary":2
}, {
"name": "B",
"salary": 5,
"sumSalary":7
},{
"name":"C",
"salary":12,
"sumSalary":19
}];
I've tried this one:
let mutatedPersons = persons.map((currentValue, index, mutatedPersons) => ({
...currentValue,
sumSalary: currentValue.name === 'A' ? currentValue.salary : mutatedPersons[index - 1].sumSalary + currentValue.salary
}))
but i keep getting this :
[
0: {name: "A", salary: 2, sumSalary: 2}
1: {name: "B", salary: 5, sumSalary: NaN}
2: {name: "C", salary: 12, sumSalary: NaN}
]
The mutatedPersons you refer to is the original array (see map's parameters), and not the updated array, which doesn't actually exist before the map end. You can cache the previous sum in an external variable (prevSumSalary), and use it as the basis of the new one:
const persons = [{ name: "A", salary: 2 }, { name: "B", salary: 5 }, { name: "C", salary: 12 }]
let prevSumSalary = 0;
const mutatedPersons = persons.map((currentValue, index) => ({
...currentValue,
sumSalary: (prevSumSalary = prevSumSalary + currentValue.salary)
}))
console.log(mutatedPersons);
Another option is to use Array.reduce(), since you have access to the accumulated values:
const persons = [{ name: "A", salary: 2 }, { name: "B", salary: 5 }, { name: "C", salary: 12 }]
const mutatedPersons = persons.reduce((r, currentValue) => [...r,
({
...currentValue,
sumSalary: currentValue.salary + (r.length && r[r.length - 1].sumSalary)
})
], [])
console.log(mutatedPersons);
You could use a closure over sum and take it for added sumSalary property.
var data = [{ name: "A", salary: 2 }, { name: "B", salary: 5 }, { name: "C", salary: 12 }],
result = data.map((sum => o => ({ ...o, sumSalary: sum += o.salary }))(0));
console.log(result);
let persons = [{
"name": "A",
"salary": 2
}, {
"name": "B",
"salary": 5
},{
"name":"C",
"salary":12
}];
let sum = 0;
persons.map(curr => {
sum += curr.salary;
curr.sumSalary = sum;
});
console.log(persons);
This is an ideal case for Array#reduce.
One can use an accumulator (the whole salarySum) to get direct access to the last salarySum value.
Whenever one thinks about an aggregate function, one should immediately think about Array#reduce in first place!
Also, I've provided you a solution which doesn't mutate the source persons but it creates a new array which has salarySum on each item:
const persons = [{
name: "A",
salary: 2
}, {
name: "B",
salary: 5
}, {
name: "C",
salary: 12
}]
const {
persons: persons_,
salarySum
} = persons.reduce((result, person) =>
result.persons.push({
...person,
salarySum: (result.salarySum += person.salary)
}) && result, {
salarySum: 0,
persons: []
})
console.log(persons_)
console.log(salarySum)

Categories

Resources