How can I extract from an array of objects some properties like without for loop, with map or filter?
Example:
obj = [
{ 'cars' : 15, 'boats' : 1, 'smt' : 0 },
{ 'cars' : 25, 'boats' : 11, 'smt' : 0 }
]
extractFunction(obj, ['cars' , 'boats']) -> { 'cars' : [15,25], 'boats' : [1,11]}
You can do it with reduce:
* As you can see, the benefit of this approach (according to the other answers) is that you loop the keys only one time.
const extractFunction = (items, keys) => {
return items.reduce((a, value) => {
keys.forEach(key => {
// Before pushing items to the key, make sure the key exist
if (! a[key]) a[key] = []
a[key].push(value[key])
})
return a
}, {} )
}
obj = [
{ 'cars' : 15, 'boats' : 1, 'smt' : 0 },
{ 'cars' : 25, 'boats' : 11, 'smt' : 0 }
]
console.log(extractFunction(obj, ['cars', 'boats']))
You could take a dynamic approach by using the keys for mapping the values.
function extractFunction(array, keys) {
return array.reduce(
(r, o) => (keys.forEach(k => r[k].push(o[k])), r),
Object.assign(...keys.map(k => ({ [k]: [] })))
);
}
console.log(extractFunction([{ cars: 15, boats: 1, smt: 0 }, { cars: 25, boats: 11, smt: 0 }], ['cars', 'boats']));
Related
Below code which I am using for creating the new array if the id is the same in arr1 and arr2. But doesn't work since arr1 and arr2 are different. array 1 has index and arr2 is without index. screenshot for your reference. Can someone help?
Note: ID in arr1 is the same as EmpId in arr2
for(let i=0; i<arr1.length; i++) {
merged.push({
...arr1[i],
...(arr2.find((itmInner) => itmInner.id === arr1[i].id))}
);
}
console.log(merged);
Array1 looks like this :
[{"Active":1,"Id":1},
{"Active":1,"Id":3},
{"Active":1,"Id":2}]
Array2 looks something like this:
Below is the sample code on how I am framing array 2:
renderElement(activity){
var arr2 = [] ;
for(var i = 0; i < activity.length; i++) {
obj = activity[i];
if(obj.Id == 28){
fetch(geturl)
.then(function (response) {
return response.json();
})
.then(function (data) {
res = data;
arr2.push(res)
})
}
else{
// Do nothing
}
}
return arr2
}
Calling Render method like below:
outputarray = currentComponent.renderElement(activity);
console.log('output', outputarray)
Expected Output:
[{"Active":1,"Id":1,"Param1": true},
{"Active":1,"Id":3}, / Keep it as such if nothing exists in other array
{"Active":1,"Id":2, "Param2": false}]
You can try this approach instead:
Example #1
const arr1 = [
{ "Active":1, "Id":1 },
{ "Active":1, "Id":3 },
{ "Active":1, "Id":2 }
];
const arr2 = [
{
0: [
{
EmpId1: 1, Param1: true
}
]
},
{
1: [
{
EmpId2: 2,Param2: false
}
]
},
{
2: [
{
EmpId3: 2
}
]
},
];
const response = arr1
.reduce((acc, value) => {
const secondaryData = arr2.map((val, index) => {
const { [`EmpId${index + 1}`]: Id, ...others } = val[Object.keys(val)][0];
return { Id, ...others };
});
const match = secondaryData.findIndex(({ Id }) => Id === value.Id);
if (match >= 0) acc.push({...value, ...secondaryData[match]})
else acc.push(value);
return acc;
}, []);
console.log(response);
Example #2
const arr1 = [
{ "Active":1, "Id":1 },
{ "Active":1, "Id":3 },
{ "Active":1, "Id":2 }
];
const arr2 = [
[
{
EmpId1: 1,
Param1: true
}
],
[
{
EmpId2: 2,
Param2: false
}
],
[
{
EmpId3: 2
}
],
]
const response = arr1
.reduce((acc, value) => {
const secondaryData = arr2.map(([val], index) => {
const { [`EmpId${index + 1}`]: Id, ...others } = val;
return { Id, ...others };
});
const match = secondaryData.findIndex(({ Id }) => Id === value.Id);
if (match >= 0) acc.push({...value, ...secondaryData[match]})
else acc.push(value);
return acc;
}, []);
console.log(response);
Basically you can create a hash map by a object property and join on that property all the arrays, i.e. reduce an array of arrays into a result object, then convert the object's values back to an array. Since each array is reduced this means each array is only traversed once O(n) and the map object provides constant time O(1) lookup to match objects. This keeps the solution closer to O(n) rather than other solutions with a nested O(n) findIndex search, which yields a solution closer to O(n^2).
const mergeByField = (...arrays) => {
return Object.values(
arrays.reduce(
(result, { data, field }) => ({
...data.flat().reduce(
(obj, el) => ({
...obj,
[el[field]]: {
...obj[el[field]],
...el
}
}),
result
)
}),
{}
)
);
};
Load each array into a payload object that specifies the field key to match on. This will return all fields used to match by, but these can safely be ignored later, or removed, whatever you need. Example:
mergeByField(
{ data: arr1, field: "Id" },
{ data: arr2, field: "EmpId" },
);
const arr1 = [
{
Active: 1,
Id: 1
},
{
Active: 1,
Id: 2
},
{
Active: 1,
Id: 3
}
];
const arr2 = [[{ EmpId: 1, Param1: true }], [{ EmpId: 3, Param2: false }]];
const mergeByField = (...arrays) => {
return Object.values(
arrays.reduce(
(result, { data, field }) => ({
...data.flat().reduce(
(obj, el) => ({
...obj,
[el[field]]: {
...obj[el[field]],
...el
}
}),
result
)
}),
{}
)
);
};
console.log(
mergeByField({ data: arr1, field: "Id" }, { data: arr2, field: "EmpId" })
);
So I have an array of statuses and I want to get distinct values based on an element property ProductOrderStatusTypeID. I have one implementation but I want the cleanest way of doing that.
My list:
[{
StatusID : 1,
Name : "Open",
ProductOrderStatusType : 'Initial'
ProductOrderStatusTypeID : 1
},{
StatusID : 3,
Name : "Closed",
ProductOrderStatusType : 'Final'
ProductOrderStatusTypeID : 2
},
{
StatusID : 3,
Name : "Pending",
ProductOrderStatusType : 'Initial'
ProductOrderStatusTypeID : 1
}]
Expected output:
[{
ProductOrderStatusType : 'Initial',
ProductOrderStatusTypeID : 1
},{
ProductOrderStatusType : 'Final',
ProductOrderStatusTypeID : 2
}]
My code:
function getDistinctProductOrderStatusType(statuses) {
return statuses.reduce(function (result, status) {
var existingStatus = result.filter(function (res) {
return res.ProductOrderStatusTypeID === status.ProductOrderStatusTypeID;
})[0];
if (!existingStatus) {
result.push({
ProductOrderStatusTypeID: status.ProductOrderStatusTypeID,
ProductOrderStatusType: status.ProductOrderStatusType
});
}
return result;
}, []);
}
const stat = [{
StatusID: 1,
Name: "Open",
type: 'Initial', // !!! I renamed properties for simplicity sake
typeID: 1
}, {
StatusID: 3,
Name: "Closed",
type: 'Final',
typeID: 2
}, {
StatusID: 3,
Name: "Pending",
type: 'Initial',
typeID: 1
}
];
const distinctStat = arr => arr.reduce((a, {type, typeID}) =>
a.some(ob => ob.typeID === typeID) ? a : a.concat({type, typeID})
, []);
console.log( distinctStat(stat) );
In case you find it more easier to name the properties you're not interested in collecting (like i.y.e: StatusID and Name) than you could go for the rest element:
const distinctStat = arr => arr.reduce((a, {StatusID, Name, ...r}) =>
a.some(ob => ob.typeID === r.typeID) ? a : a.concat(r)
, []);
(same output as above)
Info:
Although we could, in the above code Array.prototype.some()
is used instead of Array.prototype.find()
because we don't need to return the entire object but just a boolean (as quickly as possible).
I am working a demo react project, and got stuck to pick name and value of js object and push to an array. which I want to use for queryparams. The js objects looks as follow
const ingredient = [
{ salad: 0, bacon: 0 },
{ cheese: 0, meat: 0 }
]
here I want the key and the value to push to an array. ex.
const arrays = []
I try this way and did not got it correct.
=> for (let name of ingredient){
arrays.push(name + ingredient[name])
}
=> arrays
(2) ["[object Object]undefined", "[object Object]undefined"]
how do I push only the name and value correctly so I will able to use them in the url?
thanks in advance for your help.
First you have a struct problem:
Instead of
const ingredient = [
{ salad: 0, bacon: 0 },
{ cheese: 0, meat: 0 }
]
you should have
const ingredient = { salad: 0, bacon: 0, cheese: 0, meat: 0 }
or
const ingredient = [{salade:0}, {bacon:0}, {cheese:0}, {meat:0}]
Let's say you keep only an object:
const ingredient = { salad: 2, bacon: 3, cheese: 4, meat: 0 }
let toQuery = function(obj, is_str) {
let arr = Object.keys(obj).reduce((acc, key) => {
acc.push(key+"="+obj[key]);
return acc
}, [])
return is_str ? "?" + arr.join('&') : arr
}
console.log(toQuery(ingredient, false))
console.log(toQuery(ingredient, true))
Now make it works with your object (example, multi recipe)
const ingredient = [{ salad: 2, bacon: 3}, {cheese: 4, meat: 0 }]
let toQuery = function(obj, to_str) {
let arr = []
obj.forEach(o => {
Object.keys(o).forEach(k => {
arr.push(k+"="+o[k])
})
})
return to_str ? "?" + arr.join('&') : arr
}
console.log(toQuery(ingredient, false))
console.log(toQuery(ingredient, true))
Try this:
ingredient.forEach((elem) =>
{
for(key in elem)
{
arrays.push(`${key}, ${elem[key]}`);
}
});
If name you mentioned is the key of object item. you can try this.
const ingredient = [
{ salad: 0, bacon: 0 },
{ cheese: 0, meat: 0 }
]
const rs = ingredient.reduce((acc, e) =>{
for(let key in e) {
acc.push(`${key}: ${e[key]}`)
}
return acc
}, [])
console.log(rs)
I'm saving an object in state that looks like:
ingredients: {
salad: {
amount: 3,
basePrice: 1
},
cheese: {
amount: 2,
basePrice: 1.2
}
}
I want to use this in my component's state as follows
ingredients: {
salad: 3,
cheese: 2
}
I used Object.keys and map at first but it returns an Array of key value pairs instead of objects.
Although this works:
const newIngredientsObject = {};
for (const i in ingredientsObject)
newIngredientsObject[i] = ingredientsObject[i].amount;
return newIngredientsObject;
I want to find a solution without a helper method, as such:
const mapStateToProps = state => {
return {
ingredients: Object.keys(state.burger.ingredients).map(i => (
{ [i]: state.burger.ingredients[i].amount } )),
totalPrice: state.burger.totalPrice
}
};
You could map the entries of the object by using the amount property of the values and take Object.assign for getting a new object.
var ingredients = { salad: { amount: 3, basePrice: 1 }, cheese: { amount: 2, basePrice: 1.2 } },
result = Object.assign(
...Object.entries(ingredients).map(([key, value]) => ({ [key]: value.amount }))
);
console.log(result);
when you want to create one object to inside to another go this web
object 1 = {
content 1 = {
stuff = {
},
more Stuff = {
}
}
}
object 2 = {
key 1:"",
key 2:""
}
object 1.content 1 = object 2;
You asked a solution with Object.keys, here is one:
var x = {ingredients: {
salad: {
amount: 3,
basePrice: 1
},
cheese: {
amount: 2,
basePrice: 1.2
}
}}
Object.keys(x.ingredients).map((d,i)=>[d,x.ingredients[d].amount]).reduce((ac,d,i)=>(ac[d[0]]=d[1],ac),{})
//{salad: 3, cheese: 2}
The shortest I could come up with it using Object.keys and reduce
const ingredients = {
salad: {
amount: 3,
basePrice: 1
},
cheese: {
amount: 2,
basePrice: 1.2
}
};
let newObject = Object.keys(ingredients).reduce((acc, val) => {
acc[val] = ingredients[val].amount;
return acc;
}, {});
console.log(newObject);
ANSWER:
Thanks for the solutions provided by #ibowankenobi and #Nina Scholz
Reduce function:
Object.keys(ingredients)
.map( (d, i) => [d, ingredients[d].amount])
.reduce( (ac, d, i) => (ac[d[0]] = d[1], ac), {} );
Entries function:
Object.assign(...Object.entries(ingredients)
.map( ([key, value]) => ({ [key]: value.amount }) ) );
reduce: 0.0849609375ms
entries: 0.1650390625ms
forLoop: 0.07421875ms
reduce: 0.024169921875ms
entries: 0.048095703125ms
forLoop: 0.010009765625ms
reduce: 0.010009765625ms
entries: 0.016845703125ms
forLoop: 0.0078125ms
After some testing, it seems like using a for loop is the most efficient and after that the reduce function.
So given a list of items like so:
item_1 = {id:1, categories: {"category_A" => 1, "category_B" => {"sub_category_A" => 3, "sub_category_B" => 1}}}
item_2 = {id:2, categories: {"category_B" => {"sub_category_A" => 1, "sub_category_B" => 2}}}
Where the numeric value is that items order in a given sub or main category. Now, given a sub or main category, I want to sort the items by the order number. In Ruby I'd write...
# Given category_B and sub_category_A
items.sort_by { |i| i.categories["category_B"]["sub_category_A"] }
# which would return...
[item_2, item_1]
Also want to add, the key is if an item does NOT have the relevant passed category_B and sub_category_A, it should be excluded entirely from output.
You don't need jQuery; JavaScript arrays have a filter() function you can use to limit yourself to valid items and a sort() function that can take a comparing function as its argument:
var item_1 = {
id:1,
categories: {
"category_A" : 1,
"category_B" : {
"sub_category_A" : 3,
"sub_category_B" : 1
}
}
};
var item_2 = {
id:2,
categories: {
"category_B" : {
"sub_category_A" : 1,
"sub_category_B" : 2
}
}
};
var item_3 = {
id: 3,
categories : {
"category_A" : 2
}
};
[item_1,item_2,item_3].filter(function(entry) {
return entry.categories.category_B;}).sort(function(left, right) {
return left.categories["category_B"]["sub_category_A"] -
right.categories["category_B"]["sub_category_A"]
});
// or in the more readable ES6 style
[item_1,item_2,item_3]
.filter((entry) => entry.categories.category_B)
.sort((left, right) => left.categories["category_B"]["sub_category_A"] - right.categories["category_B"]["sub_category_A"]
);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
You can use Array#Sort in JavaScript.
a.categories["category_B"]["sub_category_A"] - b.categories["category_B"]["sub_category_A"]
Is the comparison of 2 elements in Array.
UPDATE: to excluded from the output. You can use Array#filter it before sorting
Let's see my example
var arr = [{
id:1,
categories:
{
"category_A" : 1,
"category_B" : {
"sub_category_A" : 3,
"sub_category_B" : 1
}
}
},{
id:3,
categories: {
"category_C" : {
"sub_category_A" : 1,
"sub_category_B" : 2
}
}
},{
id:2,
categories: {
"category_B" : {
"sub_category_A" : 1,
"sub_category_B" : 2
}
}
}];
var result = arr.filter(a => a.categories["category_B"])
.sort((a, b) => {
return a.categories["category_B"]["sub_category_A"] - b.categories["category_B"]["sub_category_A"];
})
console.log(result);
Neither ECMAScript nor jQuery have sortBy, but LoDash does and so does Underscore.
It's also not hard to supply your own:
Array.prototype.sortBy = function sortBy(keyFn) {
const compare = (a, b) => a > b ? 1 : (a < b ? -1 : 0);
return this.
map(el => [keyFn(el), el]).
sort(([a, ], [b, ]) => compare(a, b)).
map(([, el]) => el);
};
const item1 = { id: 1, categories: { category_A: 1, category_B: { sub_category_A: 3, sub_category_B: 1 }}};
const item2 = { id: 2, categories: { category_B: { sub_category_A: 1, sub_category_B: 2 }}};
const sorted = [item1, item2].sortBy(el => el.categories.category_B.sub_category_A);
console.log(sorted);