Mapping a compounded array from objects with JavaScript [duplicate] - javascript

This question already has answers here:
Create an array with same element repeated multiple times
(25 answers)
Closed 3 years ago.
I am trying to create a simngle array of multiple values obtained from an input array of objects. Ideally I'd like to use "ES5" features to do this.
I need to transform an object such as this:
{
image: "bat",
count: 5
}
into this array [bat, bat, bat, bat, bat]
I think the code is the fastest way to explain:
//game object state
var gameObj = {
objects: [
{
image: "bat",
count: 5
},
{
image: "spider",
count: 4
},
{
image: "frankie",
count: 3
}
],
imagesArr: [],
}
function gameObjectArrayBuilder(obj) {
var resultArr = [];
resultArr = obj.map(buildArr);
function buildArr(prop) {
var image = prop.image;
var count = prop.count;
while (prop.count > 0) {
prop.count--
return prop.image
}
}
return resultArr;
}
gameObj.imagesArr = gameObjectArrayBuilder(gameObj.objects);
//View result
var html = document.querySelector("#content");
html.innerHTML = gameObj.imagesArr;
console.log(gameObj.imagesArr)
//Result should be
//[bat, bat, bat, bat, bat, spider, spider, spider, spider, frankie, frankie, frankie]
<div id="content">
</div>

To obtain an array of duplicate string values (ie "bat"), where the length of the array corresponds to a supplied variable (ie "obj.count"), you could do the following:
const obj = { image: "bat", count: 5 };
const arr = Array.from({ length : obj.count }, () => (obj.image));
console.log(arr);
You could then build upon this to populate an array based on multiple input objects like this:
var gameObj = {
objects: [{
image: "bat",
count: 5
},
{
image: "spider",
count: 4
},
{
image: "frankie",
count: 3
}
],
imagesArr: [],
}
// Use reduce to obtain compounded array from all input objects
gameObj.imagesArr = gameObj.objects.reduce((acc, obj) => {
// Concat acc array with resulting array from current obj
return acc.concat(Array.from({ length: obj.count }, () => (obj.image)))
}, []);
console.log(gameObj.imagesArr);

You can use a combination of new Array and the reduce method for your case:
var gameObj = {
objects: [{
image: "bat",
count: 5
},
{
image: "spider",
count: 4
},
{
image: "frankie",
count: 3
}
],
imagesArr: [],
};
function repeatImages(acc, item, idx) {
if (!(acc instanceof Array)) {
acc = [];
}
return acc.concat(new Array(item.count).fill(item.image));
}
gameObj.imagesArr = gameObj.objects.reduce(repeatImages, []);
console.log(gameObj.imagesArr);

Solution For your example
//game object state
var gameObj = {
objects: [
{
image: "bat",
count: 5
},
{
image: "spider",
count: 4
},
{
image: "frankie",
count: 3
}
],
imagesArr: [],
}
function gameObjectArrayBuilder(obj) {
var resultArr = [];
obj.forEach((ele)=>{resultArr.push(...buildArr(ele))});
function buildArr(prop) {
var image = prop.image;
var count = prop.count;
var temp = []
while (prop.count > 0) {
prop.count--
temp.push(prop.image)
}
return temp
}
return resultArr;
}
gameObj.imagesArr = gameObjectArrayBuilder(gameObj.objects);
//View result
var html = document.querySelector("#content");
html.innerHTML = gameObj.imagesArr;
console.log(gameObj.imagesArr)
//Result should be
//[bat, bat, bat, bat, bat, spider, spider, spider, spider, frankie, frankie, frankie]
<div id="content">
</div>

You could take the count and loop this count as long as it has a truthy value.
Instead of mapping (Array#map), I would reduce (Array#reduce) the array, because the result set is an array with a count of items. In new user agents, Array#flatMap would work, but here an accumulator is necessary for getting all same items.
function gameObjectArrayBuilder(array) {
function buildArr(accumulator, object) {
var count = object.count;
while (count--) accumulator.push(object.image);
return accumulator;
}
return array.reduce(buildArr, []);
}
var gameObj = { objects: [{ image: "bat", count: 5 }, { image: "spider", count: 4 }, { image: "frankie", count: 3 }], imagesArr: [] };
gameObj.imagesArr = gameObjectArrayBuilder(gameObj.objects);
var html = document.querySelector("#content");
html.innerHTML = gameObj.imagesArr;
console.log(gameObj.imagesArr)
<div id="content"></div>

You can just use a for cycle to add items to a tmp array, then return it:
//game object state
var gameObj = {
objects: [
{
image: "bat",
count: 5
},
{
image: "spider",
count: 4
},
{
image: "frankie",
count: 3
}
],
imagesArr: [],
}
function gameObjectArrayBuilder(obj)
{
var resultArr = [];
resultArr = obj.map(buildArr);
function buildArr(prop)
{
var image = prop.image;
var count = prop.count;
// USE A TMP ARRAY
let tmpArr = []
for (let j=0; j<count; j++) {
tmpArr.push(prop.image);
}
return tmpArr
}
return resultArr;
}
gameObj.imagesArr = gameObjectArrayBuilder(gameObj.objects);
//View result
var html = document.querySelector("#content");
html.innerHTML = gameObj.imagesArr;
<div id="content">
</div>

You can use flatMap to produce the same result with less number of codes.
//game object state
var gameObj = {
objects: [
{
image: "bat",
count: 5
},
{
image: "spider",
count: 4
},
{
image: "frankie",
count: 3
}
],
imagesArr: [],
}
function gameObjectArrayBuilder(obj) {
var resultArr = obj.flatMap( f=> {
return [...Array(f.count).fill(f.image)]
});
return resultArr;
}
gameObj.imagesArr = gameObjectArrayBuilder(gameObj.objects);
//View result
var html = document.querySelector("#content");
html.innerHTML = gameObj.imagesArr;
console.log(gameObj.imagesArr)
//Result should be
//[bat, bat, bat, bat, bat, spider, spider, spider, spider, frankie, frankie, frankie]
<div id="content">
</div>

Related

Divide object array elements into groups of n each javascript

I have an Object as below:
const boxOfFruits = {
apples: [
{
name: "Kashmiri",
},
{
name: "Washington",
},
{
name: "Himalayan",
},
{
name: "Fuji",
}
],
oranges: [
{
name: "Nagpur",
},
{
name: "Clementine",
},
],
mangoes: [
{
name: "Totapuri",
},
{
name: "Alphonso",
},
{
name: "Langda",
},
],
}
I want to divide these fruits into boxes; maximum of n each, let's say where n is 3 and apples, oranges and mangoes are equally distributed.
So the output in this case would be:
box_1 = [{name: "Kashmiri"}, {name: "Nagpur"},{name: "Totapuri"}];
box_2 = [{name: "Washington"}, {name: "Clementine"},{name: "Alphonso"}];
box_3 = [{name: "Himalayan"},{name: "Langda"}, {name: "Fuji"}];
The type of fruits(apple,oranges,etc)/keys in object can increase/decrease and n is also variable. In case total fruits are less than n, then it would be just 1 box of fruits.
What I have tried so far:
Using Lodash, I am calculating the minimum and the maximum fruits in a single type:
const minFruitType = _.min(Object.values(basket).map((eachBasket: any) => eachBasket.length));
Total teams will the sum of the fruits / n
Will distribute the minimum fruits (l) in the first l boxes and fill the rest with the remaining fruits at every iteration while at the start of every iteration will calculate the minimum type of fruits again.
You can use Object.values(), array#reduce and array#forEach to transform your object.
const boxOfFruits = { apples: [ { name: "Kashmiri", }, { name: "Washington", }, { name: "Himalayan", }, ], oranges: [ { name: "Nagpur", }, { name: "Clementine", }, ], mangoes: [ { name: "Totapuri", }, { name: "Alphonso", }, { name: "Langda", }, ], },
result = Object.values(boxOfFruits).reduce((r, arr) => {
arr.forEach((o,i) => {
const key = `box_${i+1}`;
r[key] ??= r[key] || [];
r[key].push(o)
});
return r;
},{});
console.log(result);
The easiest way would be to use lodash.js's zip() function:
const boxes = _.zip( Object.values(boxOfFruits) );
Note that _.zip() will give you undefined values when the source arrays are different lengths, so you'll need/want to filter those out:
const boxes == _.zip( Object.values(boxOfFruits) )
.map(
box => box.filter(
x => x !== undefined
)
);
But that will not distribute the fruits evenly. For that, it shouldn't get much for difficult than this:
function distribute(boxOfFruits, n) {
const boxes = [];
const fruits = Object.keys(boxOfFruits);
for ( const fruit of fruits ) {
let i = 0;
const items = boxOfFruits[fruit];
for (const item of items) {
boxes[i] = !boxes[i] ?? [];
boxes[i] = boxes[i].push(item);
++i;
i = i < n ? i : 0 ;
}
}
return boxes;
}
A modified version of #Nicholas Carey's answer worked for me:
function distribute(boxOfFruits, n) {
let boxes = [];
let totalFruits = Object.values(boxOfFruits)
.reduce((content, current) => content + current.length, 0);
let maxBoxes = Math.ceil(totalFruits / 4);
Object.values(boxOfFruits).forEach((fruits) => {
let i = 0;
fruits.forEach((fruit) => {
boxes[i] ??= boxes[i] || [];
boxes[i].push(fruit);
++i;
i = i < (n+1) ? i : 0;
});
});
// Extra boxes created, redistribute them to
// starting boxes
let newBoxes = teams.slice(0, maxBoxes);
let pendingBoxes = teams.slice(maxBoxes);
let pendingFruits = pendingBoxes.flat();
let distributedBoxes = newBoxes.map((eachBox) => {
let required = n - eachBox.length;
if (required > 0) {
eachBox.push(...pendingFruits.splice(0, required));
}
return eachBox;
});
return distributedBoxes;
}
Code is pretty much the same as Nicholas's accept the below changes:
Directly fetched the values and iterated over those
empty array creation was failing, this way works
and checking on the max box size with n+1 instead of n

How to concatenate object values with same id

I have an array:
let ar = [
{
uid:1,
flat_no: 1
},
{
uid:2,
flat_no: 2
},
{
uid:1,
flat_no:3
}
];
If uid are same then I want to remove duplicate uid and concatenate its flat_no. The output array should be like this:
[
{
uid:1,
flat_no: [1,3]
},
{
uid:2,
flat_no: 2
}
];
You can use a combination of Array.reduce and Array.find.
If you find an existing item in your accumulator array, just update it's flat_no property, otherwise push it to the accumulator array.
let arr = [
{
uid: 1,
flat_no: 1
},
{
uid: 2,
flat_no: 2
},
{
uid: 1,
flat_no: 3
}
]
arr = arr.reduce((arr, item) => {
const existing = arr.find(innerItem => innerItem.uid === item.uid)
if (existing) {
existing.flat_no = Array.isArray(existing.flat_no)
? existing.flat_no
: [existing.flat_no]
existing.flat_no.push(item.flat_no)
} else {
arr.push(item)
}
return arr
}, [])
console.log(arr)
You can iterate over your array and fill an object (used as a hashmap here).
Once done, you extract the values to get your result.
let hashResult = {}
ar.forEach(element => {
if (hashResult[element.uid] == undefined) {
hashResult[element.uid] = { uid: element.uid, flat_no: [] }
}
hashResult[element.uid].flat_no.push(element.flat_no)
})
let result = Object.values(hashResult)
console.log(new Date(), result)
You can do this in a concise way with a single Array.reduce and Object.values to match your desired output:
let data = [ { uid:1, flat_no: 1 }, { uid:2, flat_no: 2 }, { uid:1, flat_no:3 } ];
const result = data.reduce((r, {uid, flat_no}) => {
r[uid] ? r[uid].flat_no = [r[uid].flat_no, flat_no] : r[uid] = {uid, flat_no}
return r
}, {})
console.log(Object.values(result))
1)Reduce the initial array to an object which has uid as the key and the flat_no as the value.
2)Then run a map on the keys to convert it into an array of objects with uid and flat_no.
1) First Step Code
let ar = [{uid:1, flat_no: 1},{uid:2, flat_no: 2},{uid:1, flat_no:3}];
let outputObj = ar.reduce((outputObj,currObj,currIndex) => {
let {uid,flat_no} = currObj
if (outputObj[uid]) {
outputObj[uid].push(flat_no)
}
else {
outputObj[uid] = [flat_no]
}
return outputObj
},{})
2)
let finalOutput = Object.keys(outputObj).map(key =>
({uid:key,flat_no:outputObj[key]}))
console.log(finalOutput)

Create new array from iterating JSON objects and getting only 1 of its inner array

See jsfiddle here: https://jsfiddle.net/remenyLx/2/
I have data that contains objects that each have an array of images. I want only the first image of each object.
var data1 = [
{
id: 1,
images: [
{ name: '1a' },
{ name: '1b' }
]
},
{
id: 2,
images: [
{ name: '2a' },
{ name: '2b' }
]
},
{
id: 3
},
{
id: 4,
images: []
}
];
var filtered = [];
var b = data1.forEach((element, index, array) => {
if(element.images && element.images.length)
filtered.push(element.images[0].name);
});
console.log(filtered);
The output needs to be flat:
['1a', '2a']
How can I make this prettier?
I'm not too familiar with JS map, reduce and filter and I think those would make my code more sensible; the forEach feels unnecessary.
First you can filter out elements without proper images property and then map it to new array:
const filtered = data1
.filter(e => e.images && e.images.length)
.map(e => e.images[0].name)
To do this in one loop you can use reduce function:
const filtered = data1.reduce((r, e) => {
if (e.images && e.images.length) {
r.push(e.images[0].name)
}
return r
}, [])
You can use reduce() to return this result.
var data1 = [{
id: 1,
images: [{
name: '1a'
}, {
name: '1b'
}]
}, {
id: 2,
images: [{
name: '2a'
}, {
name: '2b'
}]
}, {
id: 3
}, {
id: 4,
images: []
}];
var result = data1.reduce(function(r, e) {
if (e.hasOwnProperty('images') && e.images.length) r.push(e.images[0].name);
return r;
}, [])
console.log(result);
All answers are creating NEW arrays before projecting the final result : (filter and map creates a new array each) so basically it's creating twice.
Another approach is only to yield expected values :
Using iterator functions
function* foo(g)
{
for (let i = 0; i < g.length; i++)
{
if (g[i]['images'] && g[i]["images"].length)
yield g[i]['images'][0]["name"];
}
}
var iterator = foo(data1) ;
var result = iterator.next();
while (!result.done)
{
console.log(result.value)
result = iterator.next();
}
This will not create any additional array and only return the expected values !
However if you must return an array , rather than to do something with the actual values , then use other solutions suggested here.
https://jsfiddle.net/remenyLx/7/

How to add each key pair from this Object into an Array as new Objects?

http://codepen.io/leongaban/pen/xwwdXr?editors=101
I have an API that returns an Object that looks like this:
{ unsure: 5, products: 25, trend: 124 }
What I need to do is take those key/value pairs and add those into an Array so they come out like so:
catArray = [
{ unsure: 5 },
{ products: 5 },
{ trend: 5 },
]
And better yet:
catArray = [
{
name: unsure,
count: 5
},
{
name: products,
count: 15
},
{
name: trend,
count: 50
}
]
Current codepen code:
var categories = { unsure: 5, products: 25, trend: 124 }
var catArray = [];
for (var i in categories) {
console.log(i + ":" + categories[i]);
catArray[i] = categories[i];
}
// vm.categories = Object.keys(data.data.categories);
console.log('catArray =',catArray);
At the moment I'm getting back some strange Array with a length of 0, my goal is an Array that has objects with which I can then iterate and build out a list with ng-repeat in my Angular app.
Pretty close, you can do:
var data = { unsure: 5, products: 25, trend: 124 }
var newData = [];
for (var key in data) {
newData.push({
name: key,
count: data[key]
});
}
Or, a different approach
var newData = Object.keys(data).map(function(key) {
return {
name: key,
count: data[key]
}
});
Demo: http://jsfiddle.net/gnrchtvj/
You can use this way
var categories = { unsure: 5, products: 25, trend: 124 }
var catArray = [];
for (var i in categories) {
if (categories.hasOwnProperty(i)) {
alert(i + " -> " + categories[i]);
catArray.push({name:i,value:categories[i]});
}
}
// vm.categories = Object.keys(data.data.categories);
console.log(catArray);

Create object array by pushing variables?

I am trying to create something like
var[1] = {object1, object2};
var[2] = {object1, object3);
Or something like that so that I can loop over each result and get all the objects associated with that key. The problem is I am either really tried or something because I can't seem to figure out how to do that.
In PHP I would do something like
$var[$object['id']][] = object1;
$var[$object['id']][] = object2;
How can I do something like that in Javascript?
I have a list of object elements, that have a key value called id and I want to organize them all by ID. Basically...
[0] = { id: 2 },
[1] = { id: 3 },
[2] = { id: 2 },
[3] = { id: 3 }
And I want to have them organized so it is like
[0] = { { id: 2 }, { id: 2 } }
[1] = { { id: 3 }, { id: 3} }
var indexedArray = [];
for(var key in myObjects) {
var myObject = myObjects[key];
if(typeof(indexedArray[myObject.id]) === 'undefined') {
indexedArray[myObject.id] = [myObject];
}
else {
indexedArray[myObject.id].push(myObject);
}
}
console.log(indexedArray);
http://jsfiddle.net/2fr4k/
Array is defined by square brackets:
var myArray = [{ "id": 2 }, { "id": 3 }];
What you had is not a valid syntax.
Using ECMA5 methods, you could do something like this.
Javascript
var d1 = [{
id: 2
}, {
id: 3
}, {
id: 2
}, {
id: 3
}],
d2;
d2 = d1.reduce(function (acc, ele) {
var id = ele.id;
if (!acc[id]) {
acc[id] = [];
}
acc[id].push(ele);
return acc;
}, {});
d2 = Object.keys(d2).map(function (key) {
return this[key];
}, d2);
console.log(JSON.stringify(d2));
Output
[[{"id":2},{"id":2}],[{"id":3},{"id":3}]]
On jsFiddle

Categories

Resources