I have two arrays like this:
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
Now I want a result like this:
result = [ {name: "apple", prices: [{id: 1, price: 50}, {id: 1, price: 40}]}, {name: "orange", prices: [{id: 2, price: 30}]}, {name: "others", prices: [{id: null, price: 80}]}]
I want to map the elements of the array a to the name of the second array b on the basis of their ids.
Here's an approach that using reduce to build a lookup set and avoid repeated searches in b. Another reduction pass builds the result arrays by name using the lookup table. Lastly, map is used to format the result.
Time complexity is linear (three passes with a lot of constant time object lookups).
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
const lookup = b.reduce((a, e) => {
a[e.id] = e.name;
return a;
}, {});
const result = Object.entries(
a.reduce((a, e) => {
const key = lookup[e.id] || "others";
if (!(key in a)) {
a[key] = [];
}
a[key].push(e);
return a;
}, {})
).map(e => ({name: e[0], prices: e[1]}));
console.log(result);
It would be more logical to not repeat the id in the prices part of the result, since the id belongs with the name.
I would suggest using a temporary map (for efficiency):
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
const map = new Map(b.map(o => [o.id, Object.assign(o, { prices: [] })]))
.set(null, {id: null, name: "others", prices: []});
a.forEach(o => map.get(o.id).prices.push(o.price));
const result = [...map.values()];
console.log(result);
Yes, you can do it simply with a map and a filter
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
b.map(({ name, id }) => ({
name,
id,
prices: a.filter(item => item.id === id).map(({ price }) => price)
}));
You can do this with a single Array.reduce and Object.values if you start by combining the 2 arrays together:
let a = [{id: 1, price: 50}, {id: 2, price: 30}, {id: 1, price: 40}, {id: null, price: 80}];
let b = [{id: 1, name: "apple"}, {id: 2, name: "orange"}];
const result = Object.values([...b, ...a].reduce((r, c) => {
if ('name' in c || c.id == null)
r[c.id || 'others'] = ({name: c.name || 'others', prices: []})
if ('name' in c)
return r
else if (c.id != null)
r[c.id].prices.push(c)
else
r['others'].prices.push(c)
return r
}, {}))
console.log(result)
The idea is to start with the one containing the names so the grouping creates first the object groupings and then just fill the group arrays.
Related
I have 2 arrays
I want this:
[{name: 'Sweater', qty: 1}, {name: 'Skirt', qty: 3}, {name: 'Socks', qty: 2}]
but I get
qtyList = [1, 3, 2]
data = [{name: 'Sweater', qty: 1}, {name: 'Skirt', qty: 1}, {name: 'Socks', qty: 1}]
let assignQty = data.map((x, id) => {
qtyList.map((y, idx) => {
if (idx == id) return x.qty = y
})
})
console.log('assignQty', assignQty)
You're essentially zipping the 2 lists together - typically done as follows:
const qtyList = [1, 3, 2]
const data = [{name: 'Sweater', qty: 1}, {name: 'Skirt', qty: 1}, {name: 'Socks', qty: 1}]
const result = data.map((item,idx) => ({...item, qty: qtyList[idx]}))
console.log(result);
const qtyList = [1, 3, 2];
data = [{name: 'Sweater', qty: 1}, {name: 'Skirt', qty: 1}, {name: 'Socks', qty: 1}]
const updateQuantity = (list, data) => {
if (data.length !== list.length) {
return
}
for (let index = 0; index < list.length; index++) {
const element = list[index];
// udpate data
data[index] = {
name: data[index].name,
qty: element
}
}
return data;
}
console.log(updateQuantity(qtyList, data));
Use Spread syntax on item than change qty with array values
qtyList = [1, 3, 2]
data = [{name: 'Sweater', qty: 1}, {name: 'Skirt', qty: 1}, {name: 'Socks', qty: 1}]
let assignQty = data.map((item, index) => (
{
...item,
qty: qtyList[index]}
));
console.log("assignQty", assignQty );
Without using the spread operator, update the required field and then return x.
qtyList = [1, 3, 2]
data = [{name: 'Sweater', qty: 1}, {name: 'Skirt', qty: 1}, {name: 'Socks', qty: 1}]
var assignQty = data.map((x, id) => {x.qty=qtyList[id];return x;})
console.log('assignQty', assignQty)
This is my approach to your question. In your example, you were trying to map two arrays into one. This approach "loops" the data array containing the objects you want to change.
Using the index of each object it sets the item.qty to the value in qtyList[index].
qtyList = [1, 3, 2];
data = [{
name: 'Sweater',
qty: 1
}, {
name: 'Skirt',
qty: 1
}, {
name: 'Socks',
qty: 1
}];
let assignQty = data.map((item, index) => {
item.qty = qtyList[index];
return item;
});
console.log(assignQty)
I have an array as shown:
var arrOne = [{id: 3},{id: 8},{id: 12}];
And another array as shown:
var arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
I want to iterate arrTwo based on arrOne, and get the val values out of arrTwo.
So the result should be:
var result = ['Bailey', 'cathy', 'David'];
Tried concatenating .map with .filter:
arrOne.map(arOne => arrTwo.filter(artwo => {
if(arOne.id === artwo.id) {
return artwo.val
} else {
return false;
}
}));
But it gives me all, and where it is false it adds false there, which I don't want.
Any ideas where I am going wrong will be appreciated.
Editing as per norbitrial's answer:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [
{id: 1, val: 'Adam'},
{id: 3, val: 'Bailey'},
{id: 8, val: 'Cathy'},
{id: 12, val: 'David'},
{id: 15, val: 'Earl'}
];
const result = arrOne.map(({id}) => arrTwo.find(e => {
const someCond = someConditionaEval();
if(someCond && e.id === id) {
return e;
} else {
return false;
}
}).val); //this breaks
Using .map() and .find() combination:
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const result = arrOne.map(({id}) => arrTwo.find(e => e.id === id).val);
console.log(result);
I hope this helps!
You can use .filter() method on arrTwo and then using .includes() method get the filtered objects from arrTwo and then finally using .map() get only the val property values from each filtered object like:
var arrOne = [{id: 3},{id: 8},{id: 12}];
var arrTwo = [{id:1,val:"Adam"},{id:3,val:"Bailey"},{id:8,val:"Cathy"},{id:12,val:"David"},{id:15,val:"Earl"}];
var result = arrTwo.filter(a => arrOne.map(o=>o.id).includes(a.id)).map(o=>o.val)
console.log( result )
You could take an object with the values and then map the wanted values.
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }],
arrTwo = [{ id: 1, val: 'Adam' }, { id: 3, val: 'Bailey' }, { id: 8, val: 'Cathy' }, { id: 12, val: 'David' }, { id: 15, val: 'Earl' }],
values = arrTwo.reduce((r, { id, val }) => (r[id] = val, r), {}),
result = arrOne.map(({ id }) => values[id]);
console.log(result);
Create a Map of val by id from arrTwo, and then map arrOne, and extract the val from the Map using the id.
Why I prefer creating a Map/dictionary (object) instead of using Array.map() with Array.find()?
Because of the complexity - Array.map() with Array.find(), for example, is O(n * m), while creating a Map and then using Array.map() to get the values is O(n + m). However, if you've got two small arrays, this shouldn't actually hurt actual performance.
const arrOne = [{id: 3},{id: 8},{id: 12}];
const arrTwo = [{id: 1, val: 'Adam'}, {id: 3, val: 'Bailey'}, {id: 8, val: 'Cathy'}, {id: 12, val: 'David'}, {id: 15, val: 'Earl'}];
const valById = new Map(arrTwo.map(({ id, val }) => [id, val]));
const result = arrOne.map(o => valById.get(o.id));
console.log(result);
Build an object from arrTwo to gather val's in one iteration.
use map on arrOne and get val from above object.
const update = (arr1, arr2) => {
const all = Object.fromEntries(arr2.map(({ id, val }) => [id, val]));
return arr1.map(({ id }) => all[id]);
};
var arrOne = [{ id: 3 }, { id: 8 }, { id: 12 }];
var arrTwo = [
{ id: 1, val: "Adam" },
{ id: 3, val: "Bailey" },
{ id: 8, val: "Cathy" },
{ id: 12, val: "David" },
{ id: 15, val: "Earl" }
];
console.log(update(arrOne, arrTwo));
I have an object like:
const arr = {
[1]: [{id: 1, category: 1}, {id: 2, category: 2}],
[2]: [{id: 3, category: 2}, {id: 4, category: 2}],
[3]: [{id: 5, category: 3}, {id: 6, category: 3}]
}
Is it possible to move for example {id: 2, category: 2} to another place to have:
const arr = {
[1]: [{id: 1, category: 1}],
[2]: [{id: 2, category: 2}, {id: 3, category: 2}, {id: 4, category: 2}],
[3]: [{id: 5, category: 3}, {id: 6, category: 3}]
}
items with category = 2 should be in the array[2], 3 in 3 etc. It's possible to do that with Object.keys and filtering somehow? Or maybe there is a better solution?
You can use Object.keys() to iterate through arr, then using array#reduce you can group your array on the category value.
const arr = {[1]: [{id: 1, category: 1}, {id: 2, category: 2}], [2]: [{id: 3, category: 2}, {id: 4, category: 2}], [3]: [{id: 5, category: 3}, {id: 6, category: 3}]},
result = Object.keys(arr).reduce((r,k) => {
arr[k].forEach(({id, category}) => {
r[category] = r[category] || [];
r[category].push({id, category});
})
return r;
},{});
console.log(result);
You can use array#concat all the values of your object, then using array#reduce group object based on category.
const arr = {[1]: [{id: 1, category: 1}, {id: 2, category: 2}], [2]: [{id: 3, category: 2}, {id: 4, category: 2}], [3]: [{id: 5, category: 3}, {id: 6, category: 3}]},
result = [].concat(...Object.values(arr)).reduce((r,{id, category}) => {
r[category] = r[category] || [];
r[category].push({id, category});
return r;
},{});
console.log(result);
Yes, the Object.keys can be used to traverse the array. Then, using Array.splice and Array.push, you can remove item and push it to proper place. Please check the following code:
const arr = {
[1]: [{id: 1, category: 1}, {id: 2, category: 2}, {id: 7, category: 3}],
[2]: [{id: 3, category: 2}, {id: 4, category: 2}],
[3]: [{id: 5, category: 3}, {id: 6, category: 3}]
};
Object.keys(arr).forEach(function(k){
for(var j = 0; j < arr[k].length;j++) {
if(arr[k][j].category!=k) {
var removedItem = arr[k].splice(arr[k].indexOf(j), 1);
var pushedArr = arr[removedItem[0].category];
if(pushedArr) {
pushedArr.push(removedItem[0]);
j--;
}
}
}
});
console.log(arr);
I have to filter an array of objects to get certain values based on an another array and distinct also
Data
var value:any[]
var inventory = [
{id: 1, quantity: 2, GroupId: 1},
{id: 2, quantity: 0, GroupId: 2},
{id: 1, quantity: 2, GroupId: 1}
];
//data from db
value = [1,2]
My code
var data = this.inventory .filter(x => x.GroupId == this.value );
Not able to get the filtered data, but returning empty array. Thanks in advance
In your code you are comparing GroupId with an array. You should check if array contains GroupId instead.
Here is how to do it:
var data = this.inventory.filter(x => value.includes(x.GroupId));
For better support you can replace Array.prototype.includes with Array.prototype.indexOf:
var data = this.inventory.filter(x => value.indexOf(x.GroupId) !== -1);
If you want to distinct by the id field here's a solution:
var inventory = [
{id: 1, quantity: 2, GroupId: 1},
{id: 2, quantity: 0, GroupId: 2},
{id: 1, quantity: 2, GroupId: 1}
];
var value = [1,2]
var data = inventory.filter(x => value.indexOf(x.GroupId)>-1).filter((elem1, pos, arr) => arr.findIndex((elem2)=>elem2.id === elem1.id) === pos);
console.log(data);
JSFiddle example: https://jsfiddle.net/7xnybhLv/1/
You should be using includes
console.log([
{id: 1, quantity: 2, GroupId: 1},
{id: 2, quantity: 0, GroupId: 2},
{id: 3, quantity: 2, GroupId: 1}
].filter(x => [1,2].includes(x.id)));
You could use the variable directly and use Array#includes.
var inventory = [{ id: 1, quantity: 2, GroupId: 1 }, { id: 2, quantity: 0, GroupId: 2 }, { id: 3, quantity: 2, GroupId: 1 }],
value = [1, 2],
data = inventory.filter(({ GroupId }) => value.includes(GroupId));
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Is it possible to concat two arrays with objects and let the second array overwrite the first array where they have the same id:
// array 1
[
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
]
// array 2:
[
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
]
// out:
[
{id: 1, name: "newFoo"}, // overwriten by array 2
{id: 2, name: "bar"}, // not changed (from array 1)
{id: 3, name: "baz"}, // not changed (from array 1)
{id: 4, name: "y"}, // added (from array 2)
{id: 5, name: "z"} // added (from array 2)
]
If it is possible I would like to do this without the use of third party libraries
var a = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
];
var b = [
{id: 1, name: "fooboo"},
{id: 4, name: "bar"},
{id: 5, name: "baz"}
];
/* iterate through each of b, if match found in a, extend with that of a. else push into b ...*/
b.forEach(m => {
var item = a.find(n => n.id === m.id);
if(item) { return Object.assign(item, m); }
a.push(m);
});
console.log(a);
You can do
let arr1 = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
]
let arr2 = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
]
let result = arr1.concat(arr2).reduce((a, b) => {
a[b.id] = b.name;
return a;
},{})
result = Object.keys(result).map(e => {
return {id : e, name : result[e]};
});
console.log(result);
Explanation
I am using the property of objects that they don't keep duplicate keys, so for an array concated together, I reduce it to an object with id as it's key and name as its value, hence overriding all duplicates. In the next step I converted this back into an array.
Check you my solution. There is no "rewrite", i just use a second array as base and don't write value if it has same id.
let a = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
];
let b = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
];
let duplicateId;
a.forEach(aitem => {
duplicateId = false;
b.forEach(bitem => {
if (aitem.id === bitem.id)
duplicateId = true;
});
if (!duplicateId)
b.push(aitem);
});
console.log(b);
Maybe you can use Object.assign and Object.entries to achieve, lets say:
const arr1 = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
]
const arr2 = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
]
const obj3 = Object.entries(Object.assign({}, ...arr1, arr2))
.map(([prop, value]) => ({[prop]:value}));
Example:
https://jsfiddle.net/0f75vLka/
Another option would be to convert arrays to map with id as key then merge the objects and then convert it back to array.
var arr1 = [
{id: 1, name: "foo"},
{id: 2, name: "bar"},
{id: 3, name: "baz"}
];
var arr2 = [
{id: 1, name: "newFoo"},
{id: 4, name: "y"},
{id: 5, name: "z"}
];
function arr2map(arr) {
var map = {};
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
map[item.id] = item;
}
return map;
}
function map2arr(map) {
var arr = [];
for (var i in map) {
arr.push(map[i]);
}
return arr;
}
var arr1m = arr2map(arr1);
var arr2m = arr2map(arr2);
var arr3m = map2arr( Object.assign({}, arr1m, arr2m) );
//output
alert(JSON.stringify(arr3m));