Using tidy.js in javascript, string agg / concatenate in summarise after groupBy - javascript

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; }

Related

Generate array into new array

Hi guys i got a complicated case for me
I have 4 array like this
[
{
"Id":1111111,
"OptionName":"Color",
"VariantName":"White"
},
{
"Id":2222222,
"optionName":"Size",
"VariantName":"XL"
},
{
"Id":3333333,
"OptionName":"Color",
"VariantName":"GREEN"
},
{
"Id":4444444,
"optionName":"Size",
"VariantName":"L"
}
]
So i want to merge the ID like
1 on 1, 1 on 2,2 on 1, 2 on 2
The result should be like this, but depend by variant name, so colours merge to size
[
{
"Id":1111111_2222222
...
},
{
"Id":1111111_4444444,
...
},
{
"Id":3333333_2222222,
...
},
{
"Id":3333333_4444444,
...
}
]
I already found how to group them by option Name, but how to merge it?
this is the code how i group them
const filteredVariantCats = variantCats
.map((a) => a.optionName)
.filter(onlyUnique)
.map((optionName) => {
let array = []
return variantCats
.filter((a) => a.optionName === optionName)
.map((a, idx) => {
array.push(a)
return a
})
})
UPDATE RESULT THAT I WANT TO RETURN IS THIS
{
id: null,
combinationStr: a.ID+"_"+b.ID,
name: a.variantName+' ,'+ b.variantName,
hex: '#000',
stock: 0,
price: 0,
priceDiscountType: OPTION.DISCOUNT_TYPE.NONE,
priceDiscount: 0,
weight: 10,
code: '',
attributeCode: 'a',
active: true,
productId: product.id from state,
}
Assume the syntax error and mis-matched cases are just error you make while writing the example and not actual mistakes in your actual code. If I understand correctly, you want to split the array into two groups, colors and sizes. Then generate all combinations between the two. Here is an example of how to do so. Since you mark ... in your expected output, I don't know what you actually expect so can only provide the Id field.
const arr = [
{
Id: 1111111,
OptionName: "Color",
VariantName: "White"
},
{
Id: 2222222,
OptionName: "Size",
VariantName: "XL",
},
{
Id: 3333333,
OptionName: "Color",
VariantName: "GREEN"
},
{
Id: 4444444,
OptionName: "Size",
VariantName: "L",
}
];
const colors = arr.filter(it => it.OptionName == "Color");
const sizes = arr.filter(it => it.OptionName == "Size");
let results = [];
for(let color of colors)
{
for(let size of sizes)
{
results.push({Id: `${color.Id}_${size.Id}`});
}
}
console.log(results);

how to transform array into different array by extracting key values using lodash

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')
}
})

Filtering object keys in an array

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.

Using filter() in combination with includes() to get partial matches

I have an array with objects I want to search through. The searchable array looks like this:
[
{ value: 0, label: 'john' },
{ value: 1, label: 'johnny' },
{ value: 2, label: 'peter' },
{ value: 3, label: 'peterson' }
]
I search through this using the Lodash filter method:
search = (text) => {
let results = _.filter(
this.props.options,
{ label: text }
);
}
This only shows the result that exactly matches the search query (text parameter). I need to make this work with partial matches. So if I insert j or johnny it should be able to find both 'John' and 'Johnny'.
I have tried:
search = (text) => {
let results = _.filter(
this.props.options =>
this.props.options.includes({ label: text })
);
}
But, no luck. No error and no results. How can I make this work?
Since you are using includes which is a part of ES6 standat, then I would solve this task with the ES6 Array.prototype.filter instead of lodash-filter:
let search = (list, text) =>
list.filter(i => i.label.toLowerCase().includes(text.toLowerCase()));
let list = [
{ value: 0, label: 'john' },
{ value: 1, label: 'johnny' },
{ value: 2, label: 'peter' },
{ value: 3, label: 'peterson' }
];
let result = search(list, 'j');
console.log(result); // [{value: 0, label: "john"}, {value: 1, label: "johnny"}]
Also, with .toLowerCase you may use "John" instead of "john".
String#includes accepts a string as a needle. If the the needle is not a string, it's converted to string, and it the case of an object it's [object Object].
You should get the value of label, and use the string's includes method:
const options = [
{ value: 0, label: 'john' },
{ value: 1, label: 'johnny' },
{ value: 2, label: 'peter' },
{ value: 3, label: 'peterson' }
];
const search = (text) => options.filter(({ label }) => label.includes(text));
const result = search('jo');
console.log(result);
That's not how you use String.prototype.includes. You should provide a string to it not an object. And you should provide a function that wraps the call to includes:
search = (text) => {
let results = _.filter(
this.props.options, // first parameter to _.filter is the array
option => option.label.includes(text) // the second parameter is a funtion that takes an option object and returns a boolean (wether the label of this option includes the text text or not)
);
}

Convert array of objects (sort of groupby)

I call a service that returns a list of questions. Then, I regroup this question by theme.
i have this conversion :
var questions = [
{
id: 1,
label: 'lorem ispum ?',
theme: {
id: 1,
label: 'dog',
}
},
{
id: 2,
label: 'lorem ispum2 ?',
theme: {
id: 2,
label: 'horse',
}
},
{
id: 3,
label: 'lorem ispum3 ?',
theme: {
id: 1,
label: 'dog',
}
},
];
var groupByTheme = questions.reduce(function(obj,item){
obj[item.theme.label] = obj[item.theme.label] || [];
obj[item.theme.label].push(item);
return obj;
}, {});
/*var result = Object.keys(groupsByTheme).map(function(key){
return {theme: key, questions: groupsByTheme[key]};
});
*/
var result = Object.entries(groupByTheme).map(([theme, questions]) => ({ theme, questions }));
console.log(result);
It works.
But now, i would like to add a property : the id of theme.
{
"themeId": 1,
"theme": "dog",
"questions": [...]
},
{
"themeId": 2,
"theme": "horse",
"questions": [...]
}
I don't find the right way without having a missy code.
Any suggestions ?
Thanks in advance !
You could greate the missing parts by getting the result set in the final stage.
I changed the key from theme to id, because id looks more unique.
var questions = [{ id: 1, label: 'lorem ispum ?', theme: { id: 1, label: 'dog' } }, { id: 2, label: 'lorem ispum2 ?', theme: { id: 2, label: 'horse' } }, { id: 3, label: 'lorem ispum3 ?', theme: { id: 1, label: 'dog' } }],
groupByTheme = questions.reduce(function (obj, item) {
obj[item.theme.id] = obj[item.theme.id] || [];
obj[item.theme.id].push(item);
return obj;
}, {}),
result = Object.entries(groupByTheme).map(([id, questions]) => ({
id,
theme: questions[0].theme.label, // add this
questions
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I would recommend you to use lodash library for that. Then the code will be pretty clean. I'll first make the themeMap object which would be map from label to id as :
const themeMap = {};
_.forEach(questions, question => themeMap[question.theme.label] = question.theme.id);
Then we can use the groupBy and map of lodash and code will be really clean as :
const result = _.map(_.groupBy(questions, question => question.theme.label), (group, key) => {
return {questions: group, theme: key, themeId: themeMap[key]};
});
The code will be less messy.
If you don't want to use lodash then you can achieve groupBy by reduce of javascript which you already did.
When you group the questions by the theme name, you can create the exact "theme" object that you want { "themeId": 1, "theme": "dog", "questions": [] }, and push the question into the questions prop. To convert to array use Object#values.
var questions = [{"id":1,"label":"lorem ispum ?","theme":{"id":1,"label":"dog"}},{"id":2,"label":"lorem ispum2 ?","theme":{"id":2,"label":"horse"}},{"id":3,"label":"lorem ispum3 ?","theme":{"id":1,"label":"dog"}}];
var result = Object.values( // get an array of theme objects
questions.reduce(function(obj,item){
obj[item.theme.label] = obj[item.theme.label] ||
Object.assign({ questions: [] }, item.theme); // create an object that includes theme data, and an empty array for questions
obj[item.theme.label].questions.push(item); // push into questions
return obj;
}, {})
);
console.log(result);

Categories