How can I replace array item with matched object keys? - javascript

Let's say I have an array of id's
const ids = [[1, 2, 3], [2, 3], [3]]
And have array with objects that have name for each id
const obj = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
What is the most proper way to get ids = [["One", "Two", "Three"], ["Two", "Three"], ["Three"]], I'm worrying that nested mapping could cause performance issues.

Use a combination of map and find. There may be a more performant way of doing it, but I'd suggest worrying about that only if you run into performance issues :-
const ids = [[1, 2, 3], [2, 3], [3]];
const obj = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
const mapped = ids.map(arr => arr.map(id => obj.find(obj => obj.id === id).name));

const obj = [
{ id: 1, name: "One" },
{ id: 2, name: "Two" },
{ id: 3, name: "Three" },
];
const ids = [[1, 2, 3], [2, 3], [3]] ;
// dedicated object to keep association between id and names
let names = {} ;
obj.forEach( o => {
names[ o.id ] = o.name ;
} ) ;
// map each sub array content to their real name
for( let i = 0; i <= ids.length-1; i++){
ids[i] = ids[i].map( id => names[id] ) ;
}
console.log( ids ) ;
=>
[ [ 'One', 'Two', 'Three' ], [ 'Two', 'Three' ], [ 'Three' ] ]

Related

Issue with concat of arrays

I have the next code:
let arr = [{
id: 1,
name: 'test',
cars: [{
nr: [5, 4]
},
{
nr: [2]
},
{
nr: [1]
},
{
nr: [5, 4]
}
],
},
{
id: 2,
name: 'test2',
cars: [{
nr: [5, 4]
},
{
nr: [2]
},
{
nr: [8]
},
{
nr: [5, 4]
}
],
}
]
const res = arr.reduce(i => i.cars).map(i => i.nr.map(i => [].concat(i)))
console.log(res)
As a result i expect an array with concatenates values from nr. For example as a result i should get 2 arrays. One for id1 and another for id2. First array should look like this: [5,4,2,1,5,4], and second should implement the same logic.
For this i make:
const res = arr.reduce(i => i.cars).map(i=> i.nr.map(i =>[].concat(i))), but it does not work. Ho to get expected result?
You want to map first over all elements and get the array of car ids through a reduce function.
const res = arr.map(({ cars }) => cars.reduce((acc, { nr }) => [...acc, ...nr], [])));
res; // => [ [ 5, 4, 2, 1, 5, 4 ], [ 5, 4, 2, 8, 5, 4 ] ]
Let me know if that's clear enough or if you have any questions!
You can use map which will return a new array along with reduce on cars. Inside reduce call back you can concat the current nr. SInce concat returns a new array so reassigning the value to accumulator
Edit: Use another reduce function to add the values
let arr = [{
id: 1,
name: 'test',
cars: [{
nr: [5, 4]
},
{
nr: [2]
},
{
nr: [1]
},
{
nr: [5, 4]
}
],
},
{
id: 2,
name: 'test2',
cars: [{
nr: [5, 4]
},
{
nr: [2]
},
{
nr: [8]
},
{
nr: [5, 4]
}
],
}
]
const res = arr.map((item) => {
return item.cars.reduce((acc, curr) => {
acc = acc.concat(curr.nr)
return acc;
}, []).reduce((acc, curr) => {
return acc + curr;
}, 0);
});
console.log(res)
Well, It was not clear for me what you are looking for. However, here is a sample code to collect nr ids for each set separately.
let arr = [{
id: 1,
name: 'test',
cars: [{
nr: [5, 4]
},
{
nr: [2]
},
{
nr: [1]
},
{
nr: [5, 4]
}
],
},
{
id: 2,
name: 'test2',
cars: [{
nr: [5, 4]
},
{
nr: [2]
},
{
nr: [8]
},
{
nr: [5, 4]
}
],
}
]
const res = arr
.map(i => i.cars)
.map(i => i
.map(i => i.nr)
.reduce((result,i)=>result.concat(i),[]));
console.log(res)
If you want to remove duplicated items:
let arr = [{
id: 1,
name: 'test',
cars: [{
nr: [5, 4]
},
{
nr: [2]
},
{
nr: [1]
},
{
nr: [5, 4]
}
],
},
{
id: 2,
name: 'test2',
cars: [{
nr: [5, 4]
},
{
nr: [2]
},
{
nr: [8]
},
{
nr: [5, 4]
}
],
}
]
const res = arr
.map(i => i.cars)
.map(i => i
.map(i => i.nr)
.reduce(function(result,i){
i.map(item=>{
if(result.indexOf(item)<0){
result.push(item);
}
});
return result;
},[]));
console.log(res)

Return multiple sets of aggregated IDs from an array of objects

Without using JQuery or other plugins I'd like to look for instances of matching attributes from an array of objects.
For example - in this array of objects there are two attributes that appear across more than one object (A and C with 3 and 2 values respectively).
The result format I'm after would be an array of arrays containing the IDs of the objects in question (see the result line). I've tried various types of filters but to no avail.
let data = [
{ id: 1, attribute: "A" },
{ id: 2, attribute: "b" },
{ id: 3, attribute: "C" },
{ id: 4, attribute: "A" },
{ id: 5, attribute: "e" },
{ id: 6, attribute: "C" },
{ id: 7, attribute: "g" },
{ id: 8, attribute: "h" },
{ id: 9, attribute: "A" }
];
let result = [
[1, 4, 9],
[3, 6]
];
You can first use array reduce() method to group objects by attribute key and the get the required ids in a group like:
let data = [
{id:1, attribute:"A"}
, {id:2, attribute:"b"}
, {id:3, attribute:"C"}
, {id:4, attribute:"A"}
, {id:5, attribute:"e"}
, {id:6, attribute:"C"}
, {id:7, attribute:"g"}
, {id:8, attribute:"h"}
, {id:9, attribute:"A"}];
let result = Object.values(data.reduce((o, obj) => {
o[obj.attribute] = (o[obj.attribute] || []).concat(obj);
return o;
}, {})).filter(({length}) => length>1).map(x=> x.map(({id}) => id));
console.log(result)
Please use Array.reduce to make simple.
let data = [
{ id: 1, attribute: "A" },
{ id: 2, attribute: "b" },
{ id: 3, attribute: "C" },
{ id: 4, attribute: "A" },
{ id: 5, attribute: "e" },
{ id: 6, attribute: "C" },
{ id: 7, attribute: "g" },
{ id: 8, attribute: "h" },
{ id: 9, attribute: "A" }
];
let result = [
[1, 4, 9],
[3, 6]
];
let results = data.reduce((x, {id, attribute}) => {
if(!x[attribute]) x[attribute] = []
x[attribute].push(id)
return x
},{})
let finalResult = []
for( let key in results) {
const value = results[key]
if(value.length > 1) finalResult.push(value)
}
console.log(finalResult)

using array as key to loop through objects in JavaScript

I am learning JavaScript and have spent a good deal of time practicing looping through arrays and arrays of Objects. I wanted to learn how to use an array as a filter on an array of Objects. I couldn't find any articles that explained how to do this, so I had a go myself using a nested loop. However, I cannot get it to work.
Var catKey[]; is the array holding the data I want to use to filter through var posts[]; , identify which objects have a match in the Property cat: [] and return the title properties. I know I could use array.Filter but I want to be able to do this on the assumption I wont always know the number of items in the catKey array. The use case would be for a situation where I use an event handler that when a link I add is clicked on a Post in WordPress and returns the category Ids, I would then search through the list of Posts to find others that have the same category Ids. Can anyone tell me where I am going wrong.
var catKey = [2, 6];
var posts = [
{
id: 1,
cat: [1, 2, 3],
title: "Hello World"
},
{
id: 2,
cat: [5, 6, 7],
title: "Hello JavaScript"
},
{
id: 3,
cat: [8, 9],
title: "Hello Arrays!"
}
];
for (var i = 0; i < catKey.length; i++) {
for (var j = 0; j < posts.length[i]; j++) {
if (catKey[i] === posts[j].cat) {
document.write(posts[j].title);
}
}
}
To find the first entry to match your conditions you can make use of Array.prototype.find() function:
var catKey = [2, 6];
var posts = [
{ id: 1, cat: [1, 2, 3], title: "Hello World" },
{ id: 2, cat: [5, 6, 7], title: "Hello JavaScript" },
{ id: 3, cat: [8, 9], title: "Hello Arrays!" }
];
const resObj = posts
.find(p => p.cat.some(c => catKey.includes(c)))
.title;
console.log(resObj)
Or to find all, use Array.prototype.filter():
var catKey = [2, 6];
var posts = [
{ id: 1, cat: [1, 2, 3], title: "Hello World" },
{ id: 2, cat: [5, 6, 7], title: "Hello JavaScript" },
{ id: 3, cat: [8, 9], title: "Hello Arrays!" }
];
const resObjs = posts
.filter(p => p.cat.some(c => catKey.includes(c)))
.map(o => o.title);
resObjs.forEach((t) => console.log(t));
Based on your question, I assume catKey contains a whitelist of numbers that the nested cat array should match, i.e. as long as any value in the cat array is found in catKeys, you want to keep them.
In that case, you can simply use .filter() to iterate through all the posts, and evaluate if there is any intersection between the individual post's cat array against the whitelist:
var filteredPosts = posts.filter(function(post) {
return post.cat.filter(function(c) { return catKey.indexOf(c) !== -1; }).length;
});
If you want to try and write in ES6, that's also not a problem: and it's even more concise!
const filteredPosts = posts.filter(post => post.cat.filter(c => catKey.includes(c)).length);
See proof-of-concept below:
var catKey = [2, 6];
var posts = [{
id: 1,
cat: [1, 2, 3],
title: "Hello World"
},
{
id: 2,
cat: [5, 6, 7],
title: "Hello JavaScript"
},
{
id: 3,
cat: [8, 9],
title: "Hello Arrays!"
}
];
var filteredPosts = posts.filter(function(post) {
return post.cat.filter(function(c) { return catKey.indexOf(c) !== -1; }).length;
});
console.log(filteredPosts);
You can use map and find together to check the values in the array with the values in the array of objects. Using map the catKey array is iterated and for every element find is used to find that element in the array inside the object inside the array named post using the .includes() method.
var catKey = [2, 6];
var posts = [{
id: 1,
cat: [1, 2, 3],
title: "Hello World"
},
{
id: 2,
cat: [5, 6, 7],
title: "Hello JavaScript"
},
{
id: 3,
cat: [8, 9],
title: "Hello Arrays!"
}];
console.log(catKey.map((e) => posts.find((x) => x.cat.includes(e))))
var catKey = [2, 6];
var posts = [
{
id: 1,
cat: [1, 2, 3],
title: "Hello World"
},
{
id: 2,
cat: [5, 6, 7],
title: "Hello JavaScript"
},
{
id: 3,
cat: [8, 9],
title: "Hello Arrays!"
}
];
var result = posts.filter(({cat})=>{
return catKey.filter((key)=>{
return cat.includes(key)
}).length > 0
})
console.log(result);
short version
posts.filter(({ cat }) => catKey.filter(key => cat.includes(key)).length > 0);

Concat multi-dimensional array into one-dimensional array

I have an array with objects
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ];
I want a new array [ 1, 2, 3, 1, 2, 3 ].
I have tried
nodes.map(node => node.children);
but it gives me [ [ 1, 2, 3 ], [ 1, 2, 3 ] ].
I have tried
[].concat(nodes.map(node => node.children));
but it doesn't work since it is just concatenating [] with [ [ 1, 2, 3 ], [ 1, 2, 3 ] ] which is just [ [ 1, 2, 3 ], [ 1, 2, 3 ] ].
You could use Array#reduce
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ],
result = nodes.reduce((r, node) => r.concat(node.children), []);
console.log(result);
console.log([... new Set(result)]); // for unique values
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can do this with Array#reduce
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ];
var result = nodes.reduce(function(r, o) {
r = r.concat(o.children);
return r;
}, []);
console.log(result)
Another way to do this using Array#forEach:
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ]
final = []
nodes.forEach(x => final = final.concat(x.children))
console.log(final)
Another shorter way is (a little modification to what OP was trying to do):
const nodes = [ { children: [1, 2, 3] }, { children: [1, 2, 3] } ];
var result = [].concat.apply([], nodes.map(x => x.children))
console.log(result);

underscore js mapping array of objects multiple properties to new array

var items = [{
//other properties... above
item_name: [
[1],
[2, 3]
],
item_description: [
[1],
[3, 4]
],
item_quantity: [
[1],
[4, 5]
],
item_value: null,
}, {
//other properties... above
item_name: 1,
item_description: 2,
item_quantity: 3,
item_value: 4,
}, {
//other properties... above
item_name: [1, 2, 3],
item_description: [1, 2, 3],
item_quantity: [1, 2, 3],
item_value: [1, 2, 3],
}];
var itemList = [];
items.forEach(function(item) {
if (!_.isArray(item.item_name)) {
itemList.push({
name: item.item_name,
description: item.item_description,
quantity: item.item_quantity,
value: item.item_value
});
}
var names = item.item_name ? _.flatten(item.item_name) : [];
var descriptions = item.item_description ? _.flatten(item.item_description) : [];
var quantity = item.item_quantity ? _.flatten(item.item_quantity) : [];
var values = item.item_value ? _.flatten(item.item_value) : [];
names.forEach(function(name, index) {
itemList.push({
name: names[index],
description: descriptions[index],
quantity: quantity[index],
values: values[index]
});
})
});
console.log(itemList);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.1/underscore-min.js"></script>
is there a way I can perform this faster in underscore, to remove all of the flattens?
for each item in the array I am taking
item_name[i]
item_description[i]
item_quantity[i]
item_value[i]
and adding them to the itemList
item properties in items can be [[],[]] or [] or integer or null
currently it is outputting what is expected (unless a name is null and it can skip items) however I do not like all of the loops this is performing and I am wondering if I can make a better use of underscore library
You can use this:
var myKeys = ['name', 'description', 'quantity', 'value'];
var result = _.flatten(items.map(function(item) {
return _.zip.apply(_, myKeys.map(function(key) {
return _.flatten([item['item_'+key]]);
})).map(function(arr) {
return _.object(myKeys, arr);
});
}));
Demo:
var items = [{
//other properties... above
item_name: [
[1],
[2, 3]
],
item_description: [
[1],
[3, 4]
],
item_quantity: [
[1],
[4, 5]
],
item_value: null,
}, {
//other properties... above
item_name: 1,
item_description: 2,
item_quantity: 3,
item_value: 4,
}, {
//other properties... above
item_name: [1, 2, 3],
item_description: [1, 2, 3],
item_quantity: [1, 2, 3],
item_value: [1, 2, 3],
}];
var myKeys = ['name', 'description', 'quantity', 'value'];
var result = _.flatten(items.map(function(item) {
return _.zip.apply(_, myKeys.map(function(key) {
return _.flatten([item['item_'+key]]);
})).map(function(arr) {
return _.object(myKeys, arr);
});
}));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.1/underscore-min.js"></script>

Categories

Resources