I am doing an assessment right now and I am allowed to use what ever resources I want, just a preface. The prompt I was given was to use .filter to filter out all objects with out a specific key, here is the prompt...
Write a function called cookieLoversOnly that takes
in an array and filters out every object that
does NOT have the key favoriteCookie.
cookieLoversOnly should return the filtered array.
This is what I have so far...
function cookieLoversOnly(arr){
return arr.filter(e => arr[e]===favoriteCookie)
}
Here are some examples of arr.filter(e => !e.favouriteCookie)
let people = [
{
name: 'Mr Fooman',
job: 'Dog walker',
favouriteAnimal: 'Dog'
},
{
job: 'Barman',
favouriteFood: 'Cookies',
favouriteCookie: 'Double Choc Chip',
favouriteAnimal: 'Fox'
},
{
name: 'Miss Baz',
favouriteFood: 'Caesar Salad',
favouriteCookie: 'Raisin',
favouriteAnimal: 'Elephant'
}
];
let demons = [
{
name: "demon 1",
favouriteCookie: false
},
{
name: "demon 2",
favouriteCookie: true
},
{
name: "demon 3",
favouriteCookie: undefined
},
{
name: "demon 4",
favouriteCookie: null
}
];
function cookieLoversOnly(arr){
return arr.filter(e => e.favouriteCookie)
}
console.log("people:", cookieLoversOnly(people));
console.log("demons:", cookieLoversOnly(demons));
and therefore this answer is wrong, if you take the question literally.
Related
We are using https://pbeshai.github.io/tidy/ for some data manipulation in javascript. We are looking to concatenate all strings for a grouped field in a summarise() after using groupby(). Trying to do something like:
let myArr = [
{ group: 1, field: 'okay' },
{ group: 1, field: 'sir' },
{ group: 2, field: 'yes' },
{ group: 2, field: 'there' },
]
tidy(myArr,
groupBy('group', [
summarise({
allStrings: concat('field', sep=' ')
})
])
)
and get as an output:
[
{ group: 1, allStrings: 'okay sir' },
{ group: 2, allStrings: 'yes there' }
]
Not seeing anything in the docs in the summarizers section for this unfortunately. allStrings: concat('field', sep=' ') is invalid as there is no concat summarizer function in tidy.js... Is this possible in tidy.js? If not, is there a straightforward way to string_agg / concat strings within a group in javascript like this?
You're right – there is not yet a concat summarizer in tidyjs, so you need to do it manually. Here's an example of how:
const myArr = [
{ group: 1, field: 'okay' },
{ group: 1, field: 'sir' },
{ group: 2, field: 'yes' },
{ group: 2, field: 'there' },
];
const output = tidy(
myArr,
groupBy('group', [
summarize({
allStrings: (items) => items.map((d) => d.field).join(' '),
}),
])
);
This will produce the following output:
[
{"group": 1, "allStrings": "okay sir"},
{"group": 2, "allStrings": "yes there"}
]
Essentially, you write a custom summarizer that maps each item into just the string value you care about, then you join those with a ' ' to get your final concatenated string.
There are so many group array of objects by key having items either pushed into array/ concatenated/ summed/ counted/ etc examples on this site. But maybe someone will find this useful.
let myArr = [
{ group: 1, field: 'okay' },
{ group: 1, field: 'sir' },
{ group: 2, field: 'yes' },
{ group: 2, field: 'there' },
]
let obj = myArr.reduce(function(agg, item) {
// do your group by logic below this line
agg[item.group] = agg[item.group] || [];
agg[item.group].push (item.field)
// do your group by logic above this line
return agg
}, {});
// this one also useful to convert to array
let result = Object.entries(obj).map(function ([key, value]) {
return {
group: key,
allStrings: value.join(" ")
}
})
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
I'm trying to create an array from items inside objects, as well as items inside arrays inside objects in a vue app, by using foreach to loop over them. It works well when I only have one single item, but I can't figure out how to loop over an array inside the object and add all of those items to the array I'm creating.
What I have now
const examples = [
{
name: "Example 1",
type: ["Meat", "Water", "Dairy"],
showDetail: false
},
{
name: "Example 2",
type: Fruit,
showDetail: false
},
{
name: "Example 3",
type: Vegetable,
showDetail: false
}
]
new Vue({
data: {
examplesList: examples,
type: ''
},
methods: {
filterList: function() {
this.type = event.target.value;
}
},
computed: {
uniqueList: function() {
const types = [];
this.examplesList.forEach((example) => {
if (!types.includes(example.type)) {
types.push(example.type);
}
});
return types;
}
}
})
It works fine if I remove the object with the array inside of "type", and adds the Fruit and Vegetable items to the array. Any ideas?
Desired output:
["Meat", "Water", "Dairy", "Fruit", "Vegetable"]
Here is one possible solution. You'll need to translate the solution to vue, of course, but the problem here really doesn't have anything to do with vue specifically so I've shown a vanilla javascript solution just to keep things simple.
const examples = [
{
name: "Example 1",
type: ["Meat", "Water", "Dairy", "Fruit"],
showDetail: false
},
{
name: "Example 2",
type: "Fruit",
showDetail: false
},
{
name: "Example 3",
type: "Vegetable",
showDetail: false
}
];
const types = [];
examples.forEach((example) => {
const exampleTypes = Array.isArray(example.type)
? example.type
: [example.type];
for (let exampleType of exampleTypes) {
if (!types.includes(exampleType)) {
types.push(exampleType);
}
}
});
console.log(types);
Here's an abstract way of doing that using a Set. Sets guarantee unique values meaning there's no need to check if an item is present or not.
Using just an array will become increasingly expensive to check if an item was already added as it will have to scan the entire array for each includes, O(n) time complexity.
const examples = [{
name: "Example 1",
type: ["Meat", "Water", "Dairy"],
showDetail: false
},
{
name: "Example 2",
type: "Fruit",
showDetail: false
},
{
name: "Example 3",
type: "Vegetable",
showDetail: false
}
];
const typeSet = new Set();
let types;
examples.forEach((example) => {
if (Array.isArray(example.type)) {
example.type.forEach(type => {
typeSet.add(type);
});
} else {
typeSet.add(example.type);
}
});
types = [...typeSet];
console.log(types);
Here is one possible solution to achieve the desired result:
computed: {
uniqueList: function() {
return this.examplesList.reduce(
(acc, itm) => (
Array.isArray(itm.type)
? itm.type.filter(t => !acc.includes(t)).length > 0
? [
...acc,
...itm.type.filter(t => !acc.includes(t))
]
: acc
: acc.includes(itm.type)
? acc
: [...acc, itm.type]
), []
)
}
}
Explanation
reduce is used on the array this.examplesList
Each item itm is processed and acc is the accumulator/aggregator (initially set to an empty array [])
if itm.type is an Array, then
if any elements in itm.type array is not already present in acc array, include it (by using the ... spread operator)
otherwise (ie, itm.type is a string)
if it is not already in acc, then include it (again, using ... spread operator)
That's it !
Please comment if any further clarification/s or question/s.
Code snippet
const examples = [{
name: "Example 1",
type: ["Meat", "Water", "Dairy"],
showDetail: false
},
{
name: "Example 2",
type: "Fruit",
showDetail: false
},
{
name: "Example 3",
type: "Vegetable",
showDetail: false
}
];
const getUniqueTypes = (arr = examples) => (
arr.reduce(
(acc, itm) => (
Array.isArray(itm.type)
? itm.type.filter(t => !acc.includes(t)).length > 0
? [
...acc,
...itm.type.filter(t => !acc.includes(t))
]
: acc
: acc.includes(itm.type)
? acc
: [...acc, itm.type]
), []
)
);
console.log(getUniqueTypes());
Working Demo :
const examples = [{
name: "Example 1",
type: ["Meat", "Water", "Dairy"],
showDetail: false
},
{
name: "Example 2",
type: "Fruit",
showDetail: false
},
{
name: "Example 3",
type: "Vegetable",
showDetail: false
}];
let newArray = []
examples.forEach((item) => {
if (typeof(item.type) === 'object') {
item.type.forEach((elem) => {
newArray.push(elem)
})
} else {
newArray.push(item.type)
}
})
console.log(newArray)
How can I transform a big array into my won small array bu extracting keys and values using lodash.
The given array consists of nested arrays as well.
Given array ->
goals= [
{ name: 'ACTIONS', levers: [ { partName: 'Improve', statuses: [ { element 1 },{ element 2 }]}] },
{ name: 'DEFINITIONS', levers: [ { partName: 'Hardwork', statuses: [ { element 1 },{ element 2 }]}] }
]
Transform in to an array below :
resultantArray =
[
{ name: "ACTIONS",partName: "Improve", statuses: [ { element1 }, { element2 } ] },
{ name: "DEFINITIONS",partName: "hardwork", statuses: [ { element1 }, { element2 } ] }
]
Given the example, I'm assuming that array under levers always contains only one element.
There is no real need to use lodash to solve this, all you need is a .map method and an access to object keys.
It can be done in pure JS, as well as lodash. Below are examples how to do it in both:
Pure JS:
goals= [
{ name: 'ACTIONS', levers: [ { partName: 'Improve', statuses: [ { element: 1 },{ element: 2 }]}] },
{ name: 'DEFINITIONS', levers: [ { partName: 'Hardwork', statuses: [ { element: 1 },{ element: 2 }]}] }
]
resJS = goals.map(el => {
return {
name: el.name,
partName: el.levers[0].partName,
statuses: el.levers[0].statuses
}
})
console.log(JSON.stringify(resJS, undefined, 2))
Lodash:
const _ = require('lodash')
resLodash = _.map(goals, el => {
return {
name: _.get(el, 'name'),
partName: _.get(el, 'levers[0].partName'),
statuses: _.get(el, 'levers[0].statuses')
}
})
I got a very interesting task to do but end up with a bad code. It mutates initial array I want to avoid.
Here is an array example:
[
{
ID: "some id",
NAME: "some name",
PROPERTIES:
[
{
TYPE: [{UF_NAME: "some type name"}]
},
{
OTHER_TYPE:
[
{UF_NAME: "some other type name"},
{UF_NAME: "some other type name"},
]
},
...
],
...
OFFERS:
[
{
ID: "some id",
NAME: "some name",
PROPERTIES:
[
{
SIZE: [{UF_NAME: "some type name"}]
},
{
COLOR:
[
{UF_NAME: "some color 1"},
{UF_NAME: "some color 2"},
]
},
...
],
},
{
ID: "some id",
NAME: "some name",
PROPERTIES:
[
]
},
...
]
},
...
]
Here is the result I am about to achieve without mutating initial array:
[
{
ID: "some id",
NAME: "some name",
PROPERTIES:
[
{
TYPE: [{UF_NAME: "some type name"}]
},
{
OTHER_TYPE:
[
{UF_NAME: "some other type name"},
{UF_NAME: "some other type name"},
]
},
...
],
...
OFFERS:
[
{
ID: "some id",
NAME: "some name",
PROPERTIES:
[
{
SIZE: [{UF_NAME: "some type name"}]
},
{
COLOR:
[
{UF_NAME: "some color 1"},
{UF_NAME: "some color 2"},
]
},
...
],
}
]
}
]
What filter should do:
Go through whole array searching values in object properties and offers properties
Return a new copy of initial array with filtered offers
Array structure:
Product 1
-- Offer 11
-- Offer 12
-- Offer 13
Product 2
-- Offer 21
-- Offer 22
-- Offer 23
Product 3
-- Offer 31
-- Offer 32
-- Offer 33
Filtered array structure:
Product 1
-- Offer 11
Product 2
-- Offer 23
Product 3
-- Offer 31
Here is my function:
function filter (array, filter) {
return array.filter(function iter(o) {
return Object.keys(o).some(function (k) {
if (typeof o[k] === 'string' && o[k].indexOf(filter) !== -1) {
return true
}
if (Array.isArray(o[k])) {
o[k] = o[k].filter(iter)
return o[k].length
}
})
})
}
You need to rewrite the code to avoid fiter with a nested filter and reassignment of properties.
This function reduce the array and checks the properties if a wanted property is found, a new property is added to a temporary object and later pushed to the result set.
Same goes for nested arrays, these are reduced as well, because of a possible nested structure.
function filter(array, filter) {
return array.reduce(function iter(r, o) {
var temp = {};
Object.keys(o).forEach(function (k) {
if (typeof o[k] === 'string' && o[k].indexOf(filter) !== -1) {
temp[k] = o[k];
} else if (Array.isArray(o[k])) {
var filtered = o[k].reduce(iter, []);
if (filtered.length) temp[k] = filtered;
}
});
if (Object.keys(temp).length) r.push(temp);
return r;
}, []);
}
I didn't find the answer here, so please to do no write that it is duplicate!
My problem is as follows. I have such an array with objects:
Items: [
{ groups: [{…}]
id: 1
name: "Test Name"
_test: null
_test2: 0.02867696
},
{ groups: [{…}]
id: 2
name: "Test Name2"
_test: null
_test2: 0.02867696
},
]
I need to change each object fields names, such as id, name to topicId and topicName. The rest on the data I need to keep as it is and return back array with changed objects.
I wrote such a function to get access to every object:
function changeFields(data) {
for (const key of Object.keys(data)) {
data[key].map(item => {
})
}
}
but I do not know what to do next.
As per your question you are trying to modify existing array of objects. Below are the pseudo steps
Loop through array using map or any equivalent method
Clone the object
Add new property with desired key to cloned object and it's value will be value associated with existing key
Delete old key from the object
Below is the sample code for your data and in that code i am modifying key id to _id
var data = [
{
groups: [{}],
id: 1,
name: "Test Name",
_test: null,
_test2: 0.02867696
},
{
groups: [{}],
id: 2,
name: "Test Name2",
_test: null,
_test2: 0.02867696
}
];
var modifiedData = data.map((item) => {
let _item = Object.assign(item, {});
_item['_id'] = item['id'];
delete _item['id'];
return _item;
});
console.log(modifiedData);
You can use map method of array to modify each entry and reassign it.
let Items = [
{
groups: [
1, 2
],
id: 1,
name: "Test Name",
_test: null,
_test2: 0.02867696
}, {
groups: [
1, 2
],
id: 2,
name: "Test Name2",
_test: null,
_test2: 0.02867696
}
]
Items = Items.map(item => ({
group : item.group,
topicId : item.id,
topicName : item.name,
_test : item._test,
_test2 :item._test2
}))
Or you have many field in array and only want to change one or two filed you can write general solution like this :
Items = Items.map(item => {
let obj = {};
//Loop trough each fields of item
Object.keys(item).filter(key => {
switch (key) {
//Fields to be changed
case 'id':
obj['topicId'] = item['id']
break;
case 'name':
obj['topicName'] = item['name']
break;
//End
//Rest will be the same
default:
obj[key] = item[key]
}
})
//Return modified obj
return obj;
})