I have an object and I want to fill an array with the object property and repeat each property a number of times, based on its value. An example:
obj = {
watches: 3
rings: 1
}
// => ['watches', 'watches', 'watches', 'rings']
Below is what I have so far. I'm having a hard time figuring how to repeat each property based on the associated value?
function arrayBuilder(obj) {
let objToArr = [];
for (let [property, value] of Object.entries(obj)) {
objToArr.push(property);
}
return objToArr;
}
console.log(arrayBuilder({watches: 3, rings: 1}));
// => [ 'watches', 'rings' ]
You were just missing an inner loop:
function arrayBuilder(obj) {
let objToArr = [];
for (let [property, value] of Object.entries(obj)) {
for(var i=0; i<value; i++){
objToArr.push(property);
}
}
return objToArr;
}
console.log(arrayBuilder({watches: 3, rings: 1}));
You can use Array.flatMap() (note the support) with Array.fill():
const obj = {
watches: 3,
rings: 1
}
const result = Object.entries(obj).flatMap(([k, v]) => Array(v).fill(k));
console.log(result);
Or Array.reduce() with array spread, if flatMap is not supported:
const obj = {
watches: 3,
rings: 1
}
const result = Object.entries(obj)
.reduce((r, [k, v]) => [...r, ...Array(v).fill(k)], []); // or r.concat(Array(v).fill(k)) instead of the spread
console.log(result);
Just add another loop:
for (let [property, value] of Object.entries(obj)) {
for(let i = 0; i < value; i++) {
objToArr.push(property);
}
}
To achieve expected result, use below option using repeat and split methods
var obj = {
watches: 3,
rings: 1
}
let result = []
for(key in obj){
result.push(...key.concat(" ").repeat(obj[key]).trim().split(" "))
}
console.log(result)
codepen - https://codepen.io/nagasai/pen/ZVzoGB?editors=0010
Go through the keys, for each key, create an array of the required length, fill the resulting array with the names of the key, and combine the resulting arrays:
function arrayBuilder(obj) {
return [].concat.apply(
[],
Object
.entries(obj)
.map(([key, value]) => new Array(value).fill(key))
)
}
console.log(arrayBuilder({watches: 3, rings: 1}))
You can do this via Array.reduce and Object.keys:
const obj = { watches: 3, rings: 1 }
const result = Object.keys(obj).reduce((acc, key) =>
[...acc, ...new Array(obj[key]).fill(key)], [])
console.log(result)
The idea is to get the keys from the object and use them to get the length of the array needed for the Array.fill. Using Array.fill you can fill the array with the same values once you have it setup with the correct length.
Related
I have an array of objects like this:
const arr=[
{ name:"test",
class:3
},
{ name:"test2",
class:4
},
{ name:"test3",
class:5
},]
Now I have to convert it to a map like structure as shown below:
const map={
"name":["test","test2","test3"],
"class":[3,4,5]
}
I am clueless on how to make this kind of structure.Any leads will be appreciated.
If you have an arbitrary amount of keys you can use Object.entries()
to get all the key value pairs. Then just loop over all entries and add them to the final output.
const arr=[{name:"test",class:3},{name:"test2",class:4},{name:"test3",class:5}];
const map = arr.reduce((acc, obj) => {
for(const [key, value] of Object.entries(obj)) {
if(acc[key]) {
acc[key].push(value);
}else{
acc[key] = [value];
}
}
return acc;
}, {});
console.log(map);
Have a function that accepts the array and the property you're looking for, and return a new array of data using map for each property of your new object.
const arr=[{name:"test",class:3},{name:"test2",class:4},{name:"test3",class:5}];
function getData(arr, prop) {
return arr.map(obj => obj[prop]);
}
const map = {
name: getData(arr, 'name'),
class: getData(arr, 'class')
}
console.log(map);
It should be something like that
arr.reduce((res, obj) => {
Object.entries(obj).forEach(([k,v]) => {
if(!res[k]) res[k] = []
res[k].push(v)
})
return res
},{})
So I have an array looks like this:
[
{ date: '2021-07-07' },
{ date: '2021-07-07' },
{ date: '2021-07-07' },
{ date: '2021-07-08' },
{ date: '2021-07-09' },
{ date: '2021-07-10' },
{ date: '2021-07-10' }
];
How can I split into 3 array (What I mean is 1 group for unique dates, and another group for duplicate, but if there more than 1 duplicate group, it should separate into another group)
It will looks like this after split
Array 1
[{"date": "2021-07-07"},{"date": "2021-07-07"},{"date": "2021-07-07"}]
Array 2
[{"date": "2021-07-08"},{"date": "2021-07-09"}]
Array 3
[{"date": "2021-07-10"},{"date": "2021-07-10"}]
Below is my code so far, but it only work if the duplicate on have 1
const findDuplicates = arr => {
let sorted_arr = arr.slice().sort();
let result = [];
for (let i = 0; i < sorted_arr.length - 1; i++) {
if (sorted_arr[i + 1].date == sorted_arr[i].date) {
result.push(sorted_arr[i]);
}
}
return result;
};
const filterSame = arr => {
let temp = findDuplicates(arr);
const result = arr.filter(date => date.date == temp[0].date);
return result;
};
const filterUnique = array => {
let result = array.filter(
(e, i) => array.findIndex(a => a['date'] === e['date']) === i
);
let temp = findDuplicates(array);
result = result.filter(function(obj) {
return obj.date !== temp[0].date;
});
return result;
};
You could create a map, keyed by dates, and with as value an empty array. Then populate those arrays. Finally extract the arrays that have more than one element, and add to that result a combined array of those single-element arrays:
function group(data) {
let map = new Map(data.map(o => [o.date, []]));
for (let o of data) map.get(o.date).push(o);
return [
...[...map.values()].filter(({length}) => length > 1),
[...map.values()].filter(({length}) => length == 1).flat()
];
}
let data = [{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-08"},{"date":"2021-07-09"},{"date":"2021-07-10"},{"date":"2021-07-10"}];
console.log(group(data));
Explanation
let map = new Map(data.map(o => [o.date, []]));
This creates a Map. The constructor is given an array of pairs. For the example data that array looks like this:
[
["2021-07-07", []],
["2021-07-07", []],
["2021-07-07", []],
["2021-07-08", []],
["2021-07-09", []],
["2021-07-10", []],
["2021-07-10", []]
]
The Map constructor will create the corresponding Map, which really removes duplicates. You can imagine it as follows (although it is not a plain object):
{
"2021-07-07": [],
"2021-07-08": [],
"2021-07-09": [],
"2021-07-10": []
}
Then the for loop will populate these (four) arrays, so that the Map will look like this:
{
"2021-07-07": [{date:"2021-07-07"},{date:"2021-07-07"},{date:"2021-07-07"}],
"2021-07-08": [{date:"2021-07-08"}],
"2021-07-09": [{date:"2021-07-09"}],
"2021-07-10": [{date:"2021-07-10"},{date:"2021-07-10"}]
}
In the return statement the Map values are converted to an array twice. Once to filter the entries that have more than 1 element:
[
[{date:"2021-07-07"},{date:"2021-07-07"},{date:"2021-07-07"}],
[{date:"2021-07-10"},{date:"2021-07-10"}]
]
...and a second time to get those that have 1 element:
[
[{date:"2021-07-08"}],
[{date:"2021-07-09"}],
]
The second array is flattened with flat():
[
{date:"2021-07-08"},
{date:"2021-07-09"},
]
The final result concatenates the first array (with duplicate dates) with the flattened array (with unique dates), using the spread syntax (...)
This could be done in a 2 step process
a typical group by operation based on the date properties
aggregating together all the groups which only have 1 result.
const input = [{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-08"},{"date":"2021-07-09"},{"date":"2021-07-10"},{"date":"2021-07-10"}]
const grouped = input.reduce ( (acc,i) => {
if(!acc[i.date]) acc[i.date] = []
acc[i.date].push(i);
return acc;
},{});
const final = Object.values(Object.entries(grouped).reduce( (acc,[key,values]) => {
if(values.length>1) {
acc[key] = values;
}
else{
if(!acc.others) acc.others = [];
acc.others.push(values[0]);
}
return acc
},{}))
console.log(final);
Note that if you added, for example, 2021-07-11 to your original array, this would get lumped in with all the other "unique" elements. This may or may not be what you expected, but was not clear from the question.
Another option is to sort the array before grouping. If the current date being looped isn't the same as its neighbors, then it doesn't have duplicates.
const input = [{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-07"},{"date":"2021-07-08"},{"date":"2021-07-09"},{"date":"2021-07-10"},{"date":"2021-07-10"}]
input.sort((a,b) => a.date.localeCompare(b.date))
const grouped = input.reduce((acc, o, i, arr) => {
const key = o.date === arr[i-1]?.date || o.date === arr[i+1]?.date
? o.date
: 'lonely'
acc[key] ||= []
acc[key].push(o);
return acc;
}, {});
console.log(Object.values(grouped));
I have an array with the following values:
['persona1', 'Persona2', 'Persona3', 'Persona4']
And I have another array with the names of each person:
['JUAN', 'CARLOS', 'PEDRO','MATEO']
I need to generate a JSON object like the following:
{ persona1: 'JUAN', persona2: 'CARLOS', persona3: 'PEDRO', persona4: 'MATEO' }
Each value in the first array becomes the key for the corresponding value in the second array.
How can I do this in Javascript?
Loop over the array and generate the object dynamically.
let arr1 = [ 'foo', 'bar' ]
let arr2 = [ 'baz', 'qux' ]
let obj = {}
for (let i = 0; i < arr1.length; i++) obj[arr1[i]] = arr2[i];
console.log(obj);
You could use reduce here
const arr1 = ["persona1", "Persona2", "Persona3", "Persona4"];
const arr2 = ["JUAN", "CARLOS", "PEDRO", "MATEO"];
const result = arr1.reduce((acc, curr, i) => {
acc[curr] = arr2[i];
return acc;
}, {});
console.log(result);
You can create an empty object, loop over the first array, and take each item in the array as the key, and take each item of the second array as the value.
Then you can you the JSON.stringify() function to convert that object to JSON string.
const arr1 = ['persona1', 'Persona2', 'Persona3', 'Persona4']
const arr2 = ['JUAN', 'CARLOS', 'PEDRO', 'MATEO']
const output = {}
arr1.forEach((item, i) => output[item] = arr2[i])
console.log(JSON.stringify(output))
const personKeyArr = ['persona1', 'Persona2', 'Persona3', 'Persona4']
const personValArr = ['JUAN', 'CARLOS', 'PEDRO','MATEO']
const retValue = {}
personKeyArr.forEach((x,i)=>retValue[x] = [personValArr[i]])
What you want to do is a combination of zipping two arrays together and then converting the resulting pairs into the key/value entries in an object. As #Phil mentioned in their comment, the lodash library has a function to do this called zipObject, but if you don't want to load that entire library, it's not hard to create your own with reduce. Here's one version (found at https://lowrey.me/lodash-zipobject-in-es6-javascript/):
const zipObject = (props, values) => {
return props.reduce((prev, prop, i) => {
return Object.assign(prev, { [prop]: values[i] });
}, {});
};
Running it on your data:
keys = ['persona1', 'Persona2', 'Persona3', 'Persona4']
nombres = ['JUAN', 'CARLOS', 'PEDRO','MATEO']
zipObject(keys, nombres)
//=>
{
persona1: 'JUAN',
Persona2: 'CARLOS',
Persona3: 'PEDRO',
Persona4: 'MATEO'
}
I have a array of objects, for exemple:
let arr = [
{title:apple,quantity:2},
{title:banana,quantity:3},
{title:apple,quantity:5},
{title:banana,quantity:7}
];
array containe many same objects, and i want recived array with uniqe object :
let result = [
{title:apple,quantity:7},
{title:banana,quantity:10}
]
How can I do this?
You can iterate over your array and filter out all the object with same title. Then use reduce to add all the quantity and return a new object. Code is below,
let newArr = [];
arr.forEach((currentObj) => {
const alreadyExists = newArr.findIndex(item => currentObj.title === item.title) > -1;
if(!alreadyExists) {
const filtered = arr.filter(item => item.title === currentObj.title);
const newObject = filtered.reduce((acc, curr) => { return {...acc, quantity: acc.quantity += curr.quantity}}, {...currentObj, quantity: 0})
newArr.push(newObject);
}
})
console.log(newArr);
This is done on a phone so may have some typos but the gist is there:
const resultObj = arr.reduce((acc,curr) =>{
acc[curr.title] = acc[curr.title]== undefined? curr.quantity: acc[curr.title] + curr.quantity
return acc
},{})
const resultArr = Object.entries(resultObj).map([key,value]=>({title:key,quantity:value}))
You could do that in "one line" using arrow function expressions but it won't be very readable unless you know what's happening inside:
let arr = [
{title: "apple",quantity:2},
{title: "banana",quantity:3},
{title: "apple",quantity:5},
{title: "banana",quantity:7}
];
let newArr = [...arr.reduce((acc, {title, quantity}) =>
(acc.set(title, quantity + acc.get(title) || 0), acc), new Map())
].map(([title, quantity]) => ({title, quantity}));
console.log(newArr);
So basically the first part is the reduce method:
arr.reduce((acc, {title, quantity}) =>
(acc.set(title, quantity + acc.get(title) || 0), acc), new Map())
That will returns a Map object, where each title is a key (e.g. "apple") and the quantity is the value of the key.
At this point you have to convert the Map object into an array again, and you do it using the spread syntax.
After you got an array back, you will have it in the following form:
[["apple", 7], ["banana", 10]]
But that is not what you want yet, not in this form, so you have to convert it using the array's map method:
<array>.map(([title, quantity]) => ({title, quantity}))
To keep it concise it uses the destructuring assignment
I have two object arrays. I want to merge with key with value
var a = [{"fit":["34","32","30","28"],"size":["x"]}]
var b = [{"size":["s","m","xl"],"fit":["36"]}]
Expected Output should be
Obj=[{"fit":["34","32","30","28","36"],"size":["x,"s","m","xl"]}]
My Code is
let arr3 = [];
b.forEach((itm, i) => {
arr3.push(Object.assign({}, itm, a[i]));
});
alert(JSON.stringify(arr3))
it gives [{"size":["x"],"fit":["34","32","30","28"]}] which wrong.
Use Array.reduce().
// Combine into single array (spread operator makes this nice)
const myArray = [...a, ...b];
// "reduce" values in array down to a single object
const reducedArray = myArray.reduce((acc, val) => {
return [{fit: [...acc.fit, ...val.fit], size: [...acc.size, ...val.size]}];
});
Edit: if you want the reducer to merge objects regardless of what keys and fields it has then you can do by iterating over the keys of the objects and merging them dynamically:
const reducedArray = myArray.reduce((acc, val) => {
const returnObject = {};
for (const eaKey in acc) {
returnObject[eaKey] = [...acc[eaKey], ...val[eaKey]];
}
return [returnObject];
});
If the fields of the objects aren't guaranteed keys then you will need to get even more dynamic in detecting the type of merge and how to do it, but it's possible and I will leave that as an exercise for you to figure out. :)
Note that if there are duplicate values in each of the "fit" and "size" arrays, they will not be deduplicated. You'd have to do that manually as a separate step either with extra logic in the reduce function or afterwards.
combine a and b in a single array then reduce it starting with an array having an object with empty fit and size arrays:
var a = [{ fit: ["34", "32", "30", "28"], size: ["x"] }];
var b = [{ size: ["s", "m", "xl"], fit: ["36"] }];
var obj = [...a, ...b].reduce(
(acc, curr) => {
Object.keys(curr).forEach(k => {
acc[0][k] = [...new Set([...(acc[0][k] || []), ...curr[k]])];
});
return acc;
},
[{}]
);
console.log(obj);
You can create a combine function that takes fit and size from any two objects and merges them.
Use it as a reducer to combine everything.
let combine = ({fit, size}, {fit: fit2, size: size2}) =>
({ fit: [...fit, ...fit2], size: [...size, ...size2] });
let result = [...a, ...b].reduce(combine);
Example:
var a = [{"fit":["34","32","30","28"],"size":["x"]}, {"fit": ["10", "11"], "size":["xxxxxxxxl"]}]
var b = [{"size":["s","m","xl"],"fit":["36"]}];
let combine = ({fit, size}, {fit: fit2, size: size2}) =>
({ fit: [...fit, ...fit2], size: [...size, ...size2] });
let result = [...a, ...b].reduce(combine);
console.log(result);
If you don't want to use the keys directly you could try
const arr3 = b.reduce((carry, current, index) => {
Object.keys(current)
.forEach(key => {
Object.assign(carry, { [key]: Array.prototype.concat.call(current[key], a[index][key])});
});
return carry;
}, {});