javascript - search for string in array of objects - javascript

I would like to search for a string in array of objects and returns objects that matches. Trying to use es6 here.
Please find below code:
// set of keys
const defConfigs = [{
title: "Id",
key: "id"
},
{
title: "Tenant",
key: "tenant"
},
{
title: "Opened",
key: "opened"
},
{
title: "Title",
key: "title"
},
{
title: "Status",
key: "status"
},
{
title: "Priority",
key: "priority"
}
];
// items as array of objects
const items = [{
id: "INC000000004519",
title: "Follow-up after INC000000004507",
description: null,
urgency: "4-Low",
severity: "4-Minor/Localized"
},
{
id: "INC000000004515",
title: "network drop:↵Network Element CVU042_Johnstown get unsynchronized↵Network Element CVU043_Redman",
description: "Client network drop since 08:51 until 09:06, pleas…ork Element CVU045_North_Salem get unsynchronized",
urgency: "3-Medium",
severity: "3-Moderate/Limited"
},
{
id: "INC000000004088",
title: "not able to schedule GPEH in ABC",
description: "Contact: abc#xyz.com↵+14692669295↵…WCDMA, we are not able to schedule GPEH in ABC. I",
urgency: "4-Low",
severity: "4-Minor/Localized"
},
{
id: "INC000000004512",
title: "SR Updated - P3 - 2018-0427-0305 - xyz TELECOMMUNICATIONS ROMANIA S.R.L - Lost the mng connect",
description: null,
urgency: "4-Low",
severity: "4-Minor/Localized"
},
{
id: "INC000000004414",
title: "Acme incident 1 title",
description: "Acme incident 1 description",
urgency: "2-High",
severity: "1-Extensive/Widespread"
}
];
// trying to search for string in keys defined in defConfigs
items.filter(item =>
defConfigs.forEach((def) => {
if (def.key in item) {
return (item[def.key].toString().toLowerCase().match('low').length > 1);
}
}));
// always throws an error Uncaught TypeError: Cannot read property 'length' of null
console.log(items);
Here, there are 3 objects with string "low" and I expect the code to return the first item (where the "title" is "Follow-up after"); but match never returns.
How do I search for a string in array of objects and return those objects as a result ?

If you look closely you will notice that:
You do not check if .match matched (it returns null on no match; testing for null.length will throw an error)
You are checking match.length > 1... the syntax you are using will return an array with exactly one item or null
You are missing the return statement for .filter
You do not assign the return value of .filter to any variable
Here is what you need to do:
var filteredItems = items.filter(function (item) {
return defConfigs.some(function (def) {
return (def.key in item)
? item[def.key].toString().toLowerCase().match('low') !== null
: false;
});
});
console.log(filteredItems);

String.prototype.match() function will return null if there are no matches, therefore you need check this case. Next you could use Array.prototype.some() function to verify that at least one item in array is fulfilling your condition. For example:
items.filter(item =>
// check if at least one key from `defConfigs` on `item` matches 'low'
defConfigs.some((def) => {
if (def.key in item) {
const matched = item[def.key].toString().toLowerCase().match('low')
// return true if item matched
return !!matched
}
// match not found by default
return false
}));

String.prototype.match() will return null when no match found. You should strictly compare match result to null if you want it to work.
You should take a look at Array.prototype.filter. It returns a new array and won't modify the reference passed as argument, your original items are safe (for now).

In your if statement, if (def.key in item), it will check if the value of def.key is equal to the name of an attribute in item. To accomplish what you were thinking of doing, see the comments in the code below:
// set of keys
const defConfigs = [{title: "Id", key: "id"},
{title: "Tenant", key: "tenant"},
{title: "Opened", key: "opened"},
{title: "Title", key: "title"},
{title: "Status", key: "status"},
{title: "Priority", key: "priority"}];
// items as array of objects
const items = [{id: "INC000000004519", title: "Follow-up after INC000000004507", description: null, urgency: "4-Low", severity: "4-Minor/Localized"},
{id: "INC000000004515", title: "network drop:↵Network Element CVU042_Johnstown get unsynchronized↵Network Element CVU043_Redman", description: "Client network drop since 08:51 until 09:06, pleas…ork Element CVU045_North_Salem get unsynchronized", urgency: "3-Medium", severity: "3-Moderate/Limited"},
{id: "INC000000004088", title: "not able to schedule GPEH in ABC", description: "Contact: abc#xyz.com↵+14692669295↵…WCDMA, we are not able to schedule GPEH in ABC. I", urgency: "4-Low", severity: "4-Minor/Localized"},
{id: "INC000000004512", title: "SR Updated - P3 - 2018-0427-0305 - xyz TELECOMMUNICATIONS ROMANIA S.R.L - Lost the mng connect", description: null, urgency: "4-Low", severity: "4-Minor/Localized"},
{id: "INC000000004414", title: "Acme incident 1 title", description: "Acme incident 1 description", urgency: "2-High", severity: "1-Extensive/Widespread"}];
// trying to search for string in keys defined in defConfigs
items.filter(item =>
defConfigs.forEach((def) => {
//iterate through all attributes in the object
for(var key in item){
//check if the attribute exists, if it has the method 'indexOf' (if it's a string), and that it has def.key in the string
if (item[key] && item[key].indexOf && item[key].indexOf(def.key)!= -1) {
//match only accepts regular expressions which are signified in JS by enclosing the expression with forward slashes
return (item[def.key].toString().toLowerCase().match(/low/).length >1);
}
}
}));

The word "low" is found only in the urgency attribute and your defConfigs array don't have this field
So after you add it to the array do the following :
let newItems=items.filter(item => {
let count= defConfigs.reduce((acc, def) => {
if (def.key in item) {
let arr = item[def.key].toString().toLowerCase().match(/low/gi)
return acc + (arr ? 1 : 0);
}
else {
return acc;
}
}, 0)
return count >0
});
console.log(newItems);
So first define a new variable for the filtered data newItems
Then i used the reduce function to get if there is any match in the whole object
lastly used the regular expression /low/gi in the match function

Related

Javascript - transform an object of array list to another format?

So I've been stumped on this for hours and I can't really figure out an elegant solution to solve this problem. Let's say I have this:
let Fields = {
GAME: [
{ code: '{{GTAV}}', title: { en: "grnti"} },
{ code: '{{GTA5}}', title: { en: "Grand theph " } },
]
};
How can I turn this into a new format that looks like this ?
let Fields = {
tags: [
{ name: 'GAME', tags:[
{ name: 'grnti', value: "{{GTAV}}" },
{ name: 'Grand theph', value: "{{GTA5N}}" }
]},
]};
I tried to create a function to do the job , but for some reason my brain cannot seem to grasp the solution. Any help please !
A simple version of this might look like the following:
const transform = (fields) => ({
mergeTags: Object .entries (fields) .map (([name, innerFields]) => ({
name,
mergeTags: innerFields .map (({code, title: {en}}) => ({name: en, value: code}))
}))
})
const fields = {RECIPIENT: [{code: '{{RECIPIENT.LN}}', title: {en: "name"}}, {code: '{{RECIPIENT.FN}}', title: {en: "first name" }}]}
console .log (transform (fields))
But from your nested mergeTags properties, I'm guessing that there is something recursive going on. If so, we need more information about the input and output structures.
i just threw a nested reduce function together.
const transformed = Object.entries(Fields).reduce((tags, [key, value]) => {
const mergedTags = value.reduce((codes, code) => {
codes.mergeTags.push({name: code.title.en, value: code.code});
return codes;
}, {name: key, mergeTags: []})
tags.mergeTags.push(mergedTags)
return tags;
}, {mergeTags: []})
Does that work for you?
It is hard to tell exactly from your question what you are hoping to accomplish as well as the shape of your data. Based on your question though, you would probably want to use the Object.keys and map functions
let Fields = {
RECIPIENT: [
{ code: '{{RECIPIENT.LN}}', title: { en: "name" } },
{ code: '{{RECIPIENT.FN}}', title: { en: "first name" } },
]
};
// gets the keys of the 'Fields' object(in this case only 'RECIPIENT'
let newFields = Object.keys(Fields)
// each key should create a new object with the 'key' from the original object as the 'name' of the new object
.map(key => ({
name: key,
// 'Fields[key]' gets the array from the 'RECIPIENT' property and then creates a new object from each object in the original array, mapping the 'title.en' property in the original object to 'name' in the new object and 'code' in the original object to 'value' in the new object
mergeTags: Fields[key].map(property => ({
name: property.title.en,
value: property.code
}))
}));
console.log(newFields);
Here's a clean way that may seem a bit like magic, but I'll walk you through what's going on.
let Fields = {
RECIPIENT: [
{ code: '{{RECIPIENT.LN}}', title: { en: "name"} },
{ code: '{{RECIPIENT.FN}}', title: { en: "first name" } },
]
};
const { pipe, fork, map, get } = rubico
const Transformed = pipe([
Object.entries, // { RECIPIENT: [...] } => [['RECIPIENT', [...]]
fork({
mergeTags: map(fork({ // iterate through each entry ['RECIPIENT', [...]]
name: get(0), // name is the item at index 0 of each entry
mergeTags: pipe([
get(1), // mergeTags starts with index 1 of each entry, the array of code+title objects
map(fork({ // iterate through the array of code + title objects and create new objects
name: get('title.en'), // name is title.en of each object
value: get('code'), // value is title.code of each object
})),
]),
})),
}),
])(Fields)
console.log(JSON.stringify(Transformed, null, 2))
<script src="https://unpkg.com/rubico"></script>
Disclaimer: I am the author of rubico
You can examine these methods in depth at the documentation

JSON - make subchild as a condition for IF Statement

I have a JSON from an API with this content:
"items":[
{
id: *id here*,
variant: {
variant_name: *name here*,
variant_id: *id here*,
stores: {
0:
store_id: *STOREID1*,
available_for_sale: true
1:
store_id: *STOREID2*,
available_for_sale: true
}
}
}]
I want to select the items with the same store ID and Available for sale status. I have tried this but this isn't working:
if(items.variant.stores.store_id === "STOREID1" && items.variant.stores.available_for_sale === 'true'){
But it says
Uncaught TypeError: Cannot read property 'store_id' of undefined
EDIT:
I tried this:
if(items.variants.stores.store_id["******"] && items.variants.stores.available_for_sale['true']){
But it still gives the same error.
EDIT 2:
Edited the Question for more clarification.
Your JSON file is malformed. I've tried to fix it to be valid. Next time take the object you get in the console.log, and instead use JSON.stringify(jsobject) for valid JSON output instead of trying to copy paste it verbatim.
It's not entirely clear if you want the whole object, or just the stores values. But I've implemented both.
I've used Object.values, because your notation looks like objects (which would make it invalid JSON), but if they are really arrays, you shouldn't need Object.values.
data = {
"items": [{
id: "*id here*",
variant: {
variant_name: "*name here*",
variant_id: "*id here*",
stores: {
0: {
store_id: "*STOREID1*",
available_for_sale: true
},
1: {
store_id: "*STOREID2*",
available_for_sale: true
}
}
}
}]
}
// filter for and return whole object for any that have the stores in question
// use some to to search for at least one match of store values
console.log(
data.items.filter(item => Object.values(item.variant.stores).some(({
store_id,
available_for_sale
}) => store_id === '*STOREID1*' && available_for_sale))
)
// get stores values
// Using flatMap to combine all the arrays returned from each stores object.
console.log(
data.items.flatMap(item => Object.values(item.variant.stores).filter(({
store_id,
available_for_sale
}) => store_id === '*STOREID1*' && available_for_sale))
)
items is an array, you need to iterate over it and filter the items you want
const items = [{
id: 1,
variant: {
variant_name: '*name here*',
variant_id: 111,
stores: { store_id: 1111, available_for_sale: true }
}
},
{
id: 2,
variant: {
variant_name: 'name here',
variant_id: 222,
stores: { store_id: 2222, available_for_sale: true }
}
}
];
const result = items.filter(({variant: v}) => (
v.stores.store_id === 2222 && v.stores.available_for_sale
));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to do two array operations parallelly in Javascript

I have two arrays of objects having structures like this:
let groups = [
{
word: 'ok',
id: 1,
},
{
word: 'hi',
id: 2,
}
]
let words = [
{
name: 'hello',
id: 1,
meaning: 'Grreeting !',
example: 'Hello how are you ?',
groupId: 1
},
{
name: 'morning',
id: 3,
meaning: 'First sign of the day !',
example: 'Good Morning ?',
groupId: 2
}
]
A group has many words. I have an empty array results[] and I will store all matching words there.
Now I want to search a keyword hi in both, groups and words array. The current approach I am following is :
First, I will map through groups array, and if I keyword hi matched with any group name,
then I will push all the words having that groupId into my results array.
Now I will map through the words array and if keyword hi matches with any word name then I will push that into results array.
Now, I want to do both these operations parallelly. Finding all the matches into words array and groups array parallelly and pushing data to results array.
Is there any way I can do this?
This can be done by first combining both arrays and then using the filter() array prototype function.
let groups = [
{
word: 'ok',
id: 1,
},
{
word: 'hi',
id: 2,
}
]
let words = [
{
name: 'hello',
id: 1,
meaning: 'Grreeting !',
example: 'Hello how are you ?',
groupId: 1
},
{
name: 'morning',
id: 3,
meaning: 'First sign of the day !',
example: 'Good Morning ?',
groupId: 2
}
];
let search = 'hi'; // get the search term from somewhere
let result = [ ...groups, ...words ] // combine arrays into one
.filter(({ word, name }) => ( // 'extract' word/name properties
word === search || name === search // return true when word or name matches searchterm
));
Little sidenote: in this solution searchterm can't be undefined, since that will always return true in the filter function.
Although you could technically do this with a single loop, doing so would be at the cost of clarity and simplicity. Basically you'd do a loop with the index going from 0 to Math.max(groups.length, words.length) and then do your check on each array:
for (let n = 0, len = Math.max(groups.length, words.length); n < len; ++n) {
if (n < groups.length) {
// Check `groups[i]`
}
if (n < words.length) {
// Check `words[i]`
}
}
That way, you have a single loop, which is about as close to "in parallel" as you're going to get without using a web worker (which would almost certainly be overkill). But again, it doesn't buy you much, and costs you clarity and simplicity.
Using async functions for the filter operations.
Not sure what you are doing that requires this, but note that they are not running multithreaded, and it's unlikely that this will be helpful or useful in solving the actual problem. Also, the order these execute in will be deterministic for your example.
const matches = async(list, key, match) => list.filter(x => x[key] === match);
let groups = [{
word: 'ok',
id: 1,
},
{
word: 'hi',
id: 2,
}
]
let words = [{
name: 'hello',
id: 1,
meaning: 'Grreeting !',
example: 'Hello how are you ?',
groupId: 1
},
{
name: 'morning',
id: 3,
meaning: 'First sign of the day !',
example: 'Good Morning ?',
groupId: 2
}
]
let matched = [];
Promise.all([
matches(groups, 'word', 'hi').then(m => {
matched = matched.concat(m);
console.log('groups matched:', matched);
}),
matches(words, 'name', 'hello').then(m => {
matched = matched.concat(m);
console.log('words matched:', matched);
})
]).then(res => {
console.log("matched:", matched);
console.log(".all results:", [...res[0], ...res[1]]);
})
console.log("matched immediate:", matched);
setTimeout(() => console.log("matched event loop delayed:", matched), 0);

Ramda or ES6 - Return the value of an Object Property, after filtering an Array of Objects

I want to return one of the properties of an object, if the value of another property matches a constant.
Examples:
// The Array
[
{name: "Name1", description: "Name1 Description", enabled: true},
{name: "Name2", description: "Name2 Description", enabled: false},
{name: "Name3", description: "Name3 Description", enabled: false}
]
// The Constant
enum constant {
Name1 = 'Name1',
Name2 = 'Name2',
Name3 = 'Name3'
}
// What I want to return
// Value of enabled property, for the matching object
This is the code I wrote:
const filterBasedOnToggle = (featureTogglesArray: IFeatureToggle[], featureToggle: Toggle): boolean[] => {
return featureTogglesArray.filter((feature: IFeatureToggle) => feature.name === featureToggle).map(featureProperty => featureProperty.enabled);
};
This as you can see by the Typings, is returning an Array of Boolean value. I want to return the plain value. Any Ideas? Thank you!!
Didn't check if your code works, but as you said it returns a boolean array, so try using array.find to get the first match.
const MATCH = featureTogglesArray.find((feature: IFeatureToggle) => feature.name === featureToggle);
return MATCH === undefined ? false : MATCH.enabled;
Are you looking for something like this?
const filterBasedOnToggle = (toggles) => (searchName) => {
const feature = toggles .find (({name}) => name == searchName) || {}
return feature .enabled
}
const featureToggles = [{name: "Name1", description: "Name1 Description", enabled: true}, {name: "Name2", description: "Name2 Description", enabled: false}, {name: "Name3", description: "Name3 Description", enabled: false}];
const enabledByName = filterBasedOnToggle (featureToggles);
console .log (['Name1', 'Name2', 'Name3', 'Name4'] .map (enabledByName))
//~> [true, false, false, undefined]
This version uses find instead of filter to match on only the first one ... which was probably the signature issue with your version. It returns undefined if the value isn't matched, but you could easily make that false if you liked. You could of course write this with Ramda functions, but I don't see much there that would make this much simpler.

Sort an array of objects by different keys

Hope you can help me with this question I have, that I'm pretty sure it's simple but I feel I'm missing some basic concepts here.
I have an array of objects like
[{
"id":"123",
"creationUser":"user1",
"updateUser":null,
"creationDate":1517495569000,
"updateDate":null,
"text":"Hello World"
},
{
"id":"543",
"creationUser":"user2",
"updateUser":"user3",
"creationDate":1517912985769,
"updateDate":1517921704448,
"text":"Hello people"
},
{
"id":"847",
"creationUser":"user 4",
"updateUser":null,
"creationDate":null,
"updateDate":1517913015110,
"text":"Text 1"
},
{
"id":"344",
"creationUser":"user 1",
"updateUser":"central",
"creationDate":1517912979283,
"updateDate":1517923926834,
"text":"Aloha!"
}]
As you can see there are some objects that doesn't have been updated so those values are set to null, but others have been updated, so what I would like to do is to order that array by creation date unless it has been updated, which mean that the updatedDate is the key value to compare this array.
I have tried:
let comments = conversation.sort(
(a,b) => {
if (a.updateDate){
return (a.creationDate - b.updateDate);
} else {
return (b.creationDate - a.updateDate);
}
});
But obviously it only works when comparing non updated objects. I'm pretty sure I'm missing something but I'm not sure, I also thought on splitting the array into updated array and non updated array and then merege it, but it sounds a bit hacky to me.
Please please, if you can give me a hint on this, it would be great!
Thanks a lot!
You could use logical OR || and take as default creationDate.
var array = [{ id: "123", creationUser: "user1", updateUser: null, creationDate: 1517495569000, updateDate: null, text: "Hello World" }, { id: "543", creationUser: "user2", updateUser: "user3", creationDate: 1517912985769, updateDate: 1517921704448, text: "Hello people" }, { id: "847", creationUser: "user 4", updateUser: null, creationDate: null, updateDate: 1517913015110, text: "Text 1" }, { id: "344", creationUser: "user 1", updateUser: "central", creationDate: 1517912979283, updateDate: 1517923926834, text: "Aloha!" }];
array.sort((a, b) => (a.updateDate || a.creationDate) - (b.updateDate || b.creationDate));
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
So that make it so you use the defined dates by picking the one that is defined for each object.
var aDate = a.updateDate || a.creationDate
var bDate = b.updateDate || b.creationDate
return bDate - aDate
Evaluate the existence of the updateDate property first and if not exists, use the creationDate property:
var data =[{
"id":"123",
"creationUser":"user1",
"updateUser":null,
"creationDate":1517495569000,
"updateDate":null,
"text":"Hello World"
},
{
"id":"543",
"creationUser":"user2",
"updateUser":"user3",
"creationDate":1517912985769,
"updateDate":1517921704448,
"text":"Hello people"
},
{
"id":"847",
"creationUser":"user 4",
"updateUser":null,
"creationDate":null,
"updateDate":1517913015110,
"text":"Text 1"
},
{
"id":"344",
"creationUser":"user 1",
"updateUser":"central",
"creationDate":1517912979283,
"updateDate":1517923926834,
"text":"Aloha!"
}];
data.sort(function (a,b) {
var aDate = a.updateDate?a.updateDate:a.creationDate;
var bDate = b.updateDate?b.updateDate:b.creationDate;
return aDate - bDate;
});
console.log(data)
((a.updateDate || 0) - (b.updateDate || 0)) || a.creationDate - b.creationDate
That compares by creation date if both updateDates arent set, otherwise the updateSet comes first.

Categories