loop through elements of array within an object within another array - javascript

var data = [
{
Name: "HR",
popularity: 91,
keywords: ["bat", "happy", "success"]
},
{
Name: "biz",
popularity: 3,
keywords: ["sad", "code", "shady"]
},
{
Name: "xyz",
popularity: 80,
keywords: ["alphabetical", "code", "trendy"]
},
{
Name: "catty",
popularity: 536,
keywords: ["code", "startup", "cats"]
}
];
How would I write that function that simply returns all the keywords in one array, regardless of how many objects I add or delete to the data array? I don't want to manually concat each unless I can use a loop.
I mainly want to write a function that can test whether any word is in any of the keywords of the data set and then return it's name or names.

It doesn't really bring you anything to concat the keywords, if you have no link back to the original object, why not create a dictionary which key is based on the keyword, and holds the objects in an array as the value.
It's then very easy to create a search to return results based on 1 keyword
var data = [
{
Name: "HR",
popularity: 91,
keywords: ["bat", "happy", "success"]
},
{
Name: "biz",
popularity: 3,
keywords: ["sad", "code", "shady"]
},
{
Name: "xyz",
popularity: 80,
keywords: ["alphabetical", "code", "trendy"]
},
{
Name: "catty",
popularity: 536,
keywords: ["code", "startup", "cats"]
}
];
function createKeywordSet( data ) {
return data.reduce( (dict, arrayItem) => {
arrayItem.keywords.forEach( keyword => {
if (!dict[keyword]) {
dict[keyword] = [];
}
dict[keyword].push( arrayItem );
} );
return dict;
}, {});
}
function findByKeyword( keywordSet, keyword ) {
return keywordSet[keyword] || [];
}
let set = createKeywordSet( data );
console.log( findByKeyword( set, 'code' ).map( r => r.Name ) );
console.log( findByKeyword( set, 'startup' ).map( r => r.Name ) );
console.log( findByKeyword( set, 'keyword' ).map( r => r.Name ) );

A possibility, but not the only one.
Array#filter
Array#includes
Array#map
function getNames(arrayOfObjects, searchKeyword) {
return arrayOfObjects.filter((object) => {
return object.keywords.includes(searchKeyword);
}).map((object) => object.Name);
}
function includesKeyword(arrayOfObjects, searchKeyword) {
return getNames(arrayOfObjects, searchKeyword).length > 0;
}
const data = [{
Name: "HR",
popularity: 91,
keywords: ["bat", "happy", "success"]
},
{
Name: "biz",
popularity: 3,
keywords: ["sad", "code", "shady"]
},
{
Name: "xyz",
popularity: 80,
keywords: ["alphabetical", "code", "trendy"]
},
{
Name: "catty",
popularity: 536,
keywords: ["code", "startup", "cats"]
}
];
console.log(getNames(data, 'code'));
console.log(getNames(data, 'happy'));
console.log(getNames(data, 'wibble'));
console.log(includesKeyword(data, 'code'));
console.log(includesKeyword(data, 'wibble'));
Welcome to SO, in future try to provide examples of what you have tried and a full description of the problems that you are experiencing. Pseudo code is welcome if you have nothing else.

You can use the map() js function on an array.
What it does: it takes every single element of the array and executs a function.
Here is the code below that will get all the keywords and put them in the array allKeywords:
var allKeywords=[];
data.map((x)=>(allKeywords = allKeywords.concat(x.keywords)));
console.log(allKeywords);
The array allKeywords now has the following:
['bat','happy','success','sad','code','shady','alphabetical','code','trendy','code','startup','cats' ]
What happened?
I use data.map(x => x.keywords ...): to get all the inner arrays of keywords.
I concatenate all inner lists together to give the final output.
This is a functional programming way of thinking.
If you need any help understanding let me know.
Try it out!! Read a little about the map function. It is very useful.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Related

Multidimensional use of .map() array function in Javascript

I'm currently stuck in trying to transform one array to another using an array of headers as a key variable. Somewhat confusing to explain so I think the code might be better.
// Array received from backend
const projects = [
{id: 0001, name: "Test Project 1", projectType: "Quant"},
{id: 0002, name: "Test Project 2", projectType: "Qual"},
{id: 0003, name: "Test Project 3", projectType: "Quant"},
];
I use the first object of the array to create a new array for the headers using Object.keys(projects[0]).. So far so good
I would like to use the DataGrid from Material UI which requires a specific format for their rows and columns data structures. See below.
const columns-MUI = [
{
field: 'firstName',
headerName: 'First name',
width: 150,
editable: true,
},
{
field: 'lastName',
headerName: 'Last name',
width: 150,
editable: true,
},
];
const rows-MUI = [
{ id: 1, lastName: 'Snow', firstName: 'Jon', age: 35 },
{ id: 2, lastName: 'Lannister', firstName: 'Cersei', age: 42 },
{ id: 3, lastName: 'Lannister', firstName: 'Jaime', age: 45 },
{ id: 4, lastName: 'Stark', firstName: 'Arya', age: 16 },
]
I've managed to convert the headers from the result of Object.keys(projects[0]) to the desired column structure as defined by in columns-MUI with the following code when setting the state in a useEffect hook:
setNewColumns(tableHeaders?.map(( header, index ) => {
return {
field: index,
headerName: header,
width: 90
}
}));
Where I'm stuck is transforming the array from projects to rows-MUI because I somehow need to create both the key and value pair dynamically. This is what I've resulted in so far:
let tempProjectsList = [];
projectsList?.map(( project, index) => {
let projectObject = ''
tableHeaders.map(( header, index ) => {
const value = project[header] != null ? `"${project[header]}"` : ""
projectObject += `${header}:${value},`;
})
const parsedObject =`{${projectObject}}`;
tempProjectsList.push(parsedObject);
})
This process treats the key value pair as a string and parses the object once the iteration through the tableHeaders array is complete. There must be a more elegant solution though that I'm missing so I'd much appreciate the help.
I am certain that the "more elegant" solution begins like this but I think I reach the limits of my knowledge of Javascript arrays at this point:
setNewRows(projectsList?.map(( project, index) => {
return tableHeaders.map(( header, index) => {
})
}));
Let me know if this explanation isn't clear and I'll work on supplying demo of some description.

Trying to return a filter() value from an array of objects based on conditionals

I'm trying to return only a part of my object as an array based on conditionals but I'm kinda stumped.
I have an array of objects and I want to return an array of names for each key : value they fit in.
I do get only the unique pairings but I'm returning the food key:value as well and all of it is still inside an object not a new array. Some insight would be greatly appreciated. Newer to coding.
const organizeNames = function (foods) {
let foodNames = foods.filter((names) => {
if (names.food === 'oranges') {
return names.name;
}
});
console.log(foodNames);
};
console.log(
organizeNames([
{ name: 'Samuel', food: 'oranges' },
{ name: 'Victoria', food: 'pizza' },
{ name: 'Karim', food: 'pizza' },
{ name: 'Donald', food: 'pizza' },
])
);
You're really close here. What you need to incorporate is .map() to map your list of objects to a list of names. Your filter part works, partly by accident, so I've fixed it to be more correct (return names.food === 'oranges'), and then once you have your list of objects that match 'oranges' for their food, you map that filtered list into a list of names by doing .map(names => names.name)
const organizeNames = function (foods) {
let foodNames = foods.filter((names) => {
// only keep items whose .food property === 'oranges'
return names.food === 'oranges'; // returns true or false
}).map(names => {
// map the filtered list of objects into a list of names by
// returning just the object's .name property
return names.name;
});
return foodNames;
};
console.log(
organizeNames([
{ name: 'Samuel', food: 'oranges' },
{ name: 'Victoria', food: 'pizza' },
{ name: 'Karim', food: 'pizza' },
{ name: 'Donald', food: 'pizza' },
])
);
The filter() callback function should just return true or false. Then you can use map() to get a specific property from each of the filtered items.
const organizeNames = function(foods) {
let foodNames = foods.filter((names) => names.food == 'oranges').map(names => names.name);
return foodNames;
}
console.log(
organizeNames([{
name: 'Samuel',
food: 'oranges'
},
{
name: 'Victoria',
food: 'pizza'
},
{
name: 'Karim',
food: 'pizza'
},
{
name: 'Donald',
food: 'pizza'
},
])
);

how do i create a new object based off an array and another object?

I am trying to create a new object based off an existing array. I want to create a new object that show below
{ jack: 'jack', content: 'ocean'},
{ marie: 'marie', content: 'pond'},
{ james: 'james', content: 'fish biscuit'},
{paul: 'paul', content: 'cake'}
const words = ['jack','marie','james','paul']
const myUsers = [
{ name: 'jack', likes: 'ocean' },
{ name: 'marie', likes: 'pond' },
{ name: 'james', likes: 'fish biscuits' },
{ name: 'paul', likes: 'cake' }
]
const usersByLikes = words.map(word => {
const container = {};
container[word] = myUsers.map(user => user.name);
container.content = myUsers[0].likes;
return container;
})
I am not getting the correct object, but instead it returns a list.
[ { jack: [ 'shark', 'turtle', 'otter' ], content: 'ocean'}, { marie: [ 'shark', 'turtle', 'otter' ], content: 'ocean' },
{ james: [ 'shark', 'turtle', 'otter' ], content: 'ocean' },
{ paul: [ 'shark', 'turtle', 'otter' ], content: 'ocean'} ]
What is the role of words array? I think the below code will work.
const result = myUsers.map(user => ({
[user.name]: user.name,
content: user.likes
}));
console.log('result', result);
In case, if want to filter the users in word array then below solution will work for you.
const result = myUsers.filter(user => {
if (words.includes(user.name)) {
return ({
[user.name]: user.name,
content: user.likes
})
}
return false;
});
You can achieve your need with a single loop.
The answer #aravindan-venkatesan gave should give you the result you are looking for. However important to consider:
When using .map() javascript returns an array of the same length, with whatever transformations you told it to inside map().
If you want to create a brand new object, of your own construction. Try using .reduce(). This allows you to set an input variable, i.e: object, array or string.
Then loop over, and return exactly what you want, not a mapped version of the old array.
See here for more details:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

Comparing two arrays with field in common then pushing to a new array with corresponding grouped fields

general programming problem here.
I have this array called SPACES
[
{
_id: 5e1c4689429a8a0decf16f69,
challengers: [
5dfa24dce9cbc0180fb60226,
5dfa26f46719311869ac1756,
5dfa270c6719311869ac1757
]
},
{
_id: 5e1c4eb9c9461510407d5e81,
challengers: [ 5dfa24dce9cbc0180fb60226, 5dfa26f46719311869ac1756 ],
}
]
And this array called USERS
[
{
_id: 5dfa24dce9cbc0180fb60226,
name: 'Account 1',
email: 'account1#gmail.com',
spaces: [ 5e1c4689429a8a0decf16f69, 5e1c4eb9c9461510407d5e81 ],
},
{
_id: 5dfa26f46719311869ac1756,
name: 'Account 2',
email: 'account2#gmail.com',
spaces: [ 5e1c4689429a8a0decf16f69, 5e1c4eb9c9461510407d5e81 ]
},
{
_id: 5dfa270c6719311869ac1757,
name: 'Account 3',
email: 'account3#gmail.com',
spaces: [ 5e1c4689429a8a0decf16f69 ]
}
]
What I want to do, is go through both, and instead of having the SPACES.challengers array be just IDS, I would like the array to contain each USER object.
So for example, if the USER has an ID that is inside the SPACES.challengers array, then push the user into that array (which will then be the entire object).
SO FAR I have tried this (I am not very good yet):
users.map( ( user ) => {
spaces.map( ( space ) => {
if ( user.spaces.includes( space._id ) ) {
space.challengers.push(user)
}
} );
} );
However, I am not getting inside the IF block. (Even if I did, not sure if it would work OR if this is even how to do it). It feels Odd doing double maps, as I get so many iterations, and it duplicates my push (cause I have no logic to see if it just has been pushed).
Assuming every entry in the Users array has a unique ID, we can build a Hashmap to store (id, index) pairs in order to search efficiently for an ID from Users array while looping through Spaces array.
let spaces = [{_id: '5e1c4689429a8a0decf16f69',challengers: ['5dfa24dce9cbc0180fb60226', '5dfa26f46719311869ac1756', '5dfa270c6719311869ac1757']},{_id: '5e1c4eb9c9461510407d5e81',challengers: [ '5dfa24dce9cbc0180fb60226', '5dfa26f46719311869ac1756' ],}]
let users = [{_id: '5dfa24dce9cbc0180fb60226',name: 'Account 1',email: 'account1#gmail.com',spaces: [ '5e1c4689429a8a0decf16f69', '5e1c4eb9c9461510407d5e81' ],},{_id: '5dfa26f46719311869ac1756',name: 'Account 2',email: 'account2#gmail.com',spaces: [ '5e1c4689429a8a0decf16f69', '5e1c4eb9c9461510407d5e81' ]},{_id: '5dfa270c6719311869ac1757',name: 'Account 3',email: 'account3#gmail.com',spaces: [ '5e1c4689429a8a0decf16f69' ]}]
let IDIndexMapping = {} // To store (_id, index) pairs, in order to improve search efficiency
for(let index in users) // Iterate through Users array using index
IDIndexMapping[users[index]._id] = index; // store (_id, index) pair in IDIndexMapping
// I'm avoiding using `map` and using vanilla `for` loop for space efficiency
// as map returns a new array but with `for` loop, we can perform changes in-place
for(let outerIndex in spaces){ // Iterate through `spaces` array using index
let challengers = spaces[outerIndex].challengers; // Get challengers array
for(let innerIndex in challengers){ // Iterate through challengers array using index
let ID = challengers[innerIndex]; // Get ID
if(ID in IDIndexMapping) // If ID exists in IDIndexMapping
spaces[outerIndex].challengers[innerIndex] = users[IDIndexMapping[ID]]; // Change ID to actual User object
}
}
console.log(spaces)
Output
[ { _id: '5e1c4689429a8a0decf16f69',
challengers: [ [Object], [Object], [Object] ] },
{ _id: '5e1c4eb9c9461510407d5e81',
challengers: [ [Object], [Object] ] } ]
.map and .find should work here. keep it simple.
var spaces = [
{
_id: "5e1c4689429a8a0decf16f69",
challengers: [
"5dfa24dce9cbc0180fb60226",
"5dfa26f46719311869ac1756",
"5dfa270c6719311869ac1757"
]
},
{
_id: "5e1c4eb9c9461510407d5e81",
challengers: ["5dfa24dce9cbc0180fb60226", "5dfa26f46719311869ac1756", "some non existent"]
}
],
users = [
{
_id: "5dfa24dce9cbc0180fb60226",
name: "Account 1",
email: "account1#gmail.com",
spaces: ["5e1c4689429a8a0decf16f69", "5e1c4eb9c9461510407d5e81"]
},
{
_id: "5dfa26f46719311869ac1756",
name: "Account 2",
email: "account2#gmail.com",
spaces: ["5e1c4689429a8a0decf16f69", "5e1c4eb9c9461510407d5e81"]
},
{
_id: "5dfa270c6719311869ac1757",
name: "Account 3",
email: "account3#gmail.com",
spaces: ["5e1c4689429a8a0decf16f69"]
}
],
result = spaces.map(({ _id, challengers }) => ({
_id,
challengers: challengers.map(challenger =>
users.find(user => user._id === challenger)
).filter(row => row)
}));
console.log(JSON.stringify(result, null, 2));
You can create a map of challengers for look-up and then put them in spaces.
//create user map for look-up
userMap = users.reduce((res, val) => ({
...res,
[val._id]: val
}), {});
//change challenger id with user object
inflatedSpaces = spaces.map(s => ({ ...s, challengers: s.challengers.map(c => userMap[c]) }));
You could map the users with a Map.
Beside the destructuring of the object for mapping this answer uses for this part
challengers: challengers.map(
Map.prototype.get, // cb with a prototype and using `this`
new Map(users.map(o => [o._id, o])) // thisArg
)
the above mentioned Map in two parts.
The lower part generates an instance of Map where _id of the users items is used as key and the whole object as value. This instance is uses as thisArg of Array#map, the second parameter.
The upper part is a prototype of Map, used as callback. And while an this object is supplied, a binding (Function#bind) is not necessary.
var spaces = [{ _id: '5e1c4689429a8a0decf16f69', challengers: ['5dfa24dce9cbc0180fb60226', '5dfa26f46719311869ac1756', '5dfa270c6719311869ac1757'] }, { _id: '5e1c4eb9c9461510407d5e81', challengers: ['5dfa24dce9cbc0180fb60226', '5dfa26f46719311869ac1756'] }],
users = [{ _id: '5dfa24dce9cbc0180fb60226', name: 'Account 1', email: 'account1#gmail.com', spaces: ['5e1c4689429a8a0decf16f69', '5e1c4eb9c9461510407d5e81'] }, { _id: '5dfa26f46719311869ac1756', name: 'Account 2', email: 'account2#gmail.com', spaces: ['5e1c4689429a8a0decf16f69', '5e1c4eb9c9461510407d5e81'] },{ _id: '5dfa270c6719311869ac1757', name: 'Account 3', email: 'account3#gmail.com', spaces: ['5e1c4689429a8a0decf16f69'] }],
result = spaces.map(({ _id, challengers }) => ({
_id,
challengers: challengers.map(
Map.prototype.get,
new Map(users.map(o => [o._id, o]))
)
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

how to loop through multiple arrays inside an array and filter a value from it-Javascript

I'm using EXTJS framework for my code.
below is my array structure:
data = [{
id: 22,
rows: [{
id: "673627",
name: "ABS",
address: "536street"
}, {
id: "333",
name: "TEST$$",
address: "536street"
}, {
id: "999",
name: "TEST$$",
address: "536street"
}]
}, {
id: 33,
rows: [{
id: "899",
name: "TES",
address: "536street"
}, {
id: "333",
name: "TEST$$",
address: "536street"
}, {
id: "999",
name: "TES673",
address: "536street"
}]
}]
Now I want to filter the name from this array, whose value I'm comparing with say "TEST$$".
I'm doing this;
Ext.each(data, function(item) {
filter = item.rows.filter(function(name) {
return name.name === "TEST$$";
}, this);
}, this);
console.log(filter);
In this case, it returns only 1 match, where as I have 3 matches for this particular value. It returns the match from the last item in the data array and hence I dont get all the matching values, any idea how this can be looped to get all values matching?
thx!
You're reassigning the filter variable on every iteration over the data array:
filter = item.rows.filter(function(name) {
return name.name === "TEST$$";
}, this);
On the last iteration, there is only one match, the one with id of 333, so that's the only one that you see after running the Ext.each. Try pushing to an external array that doesn't get overwritten instead:
const testItems = [];
Ext.each(data, function(item) {
const filtered = item.rows.filter(row => row.name === "TEST$$")
testItems.push(...filtered);
});
console.log(testItems);
Note that there's no need to pass along the this context.
Another option is to flatMap to extract all rows to a single array first:
const output = data
.flatMap(({ rows }) => rows)
.filter(({ name }) => name === 'TEST$$');

Categories

Resources