I have an array of objects something like this:
array_ = [
{id: "id_one", name:"name_one"},
{id: "id_two", name:"name_two"},
{
id:"id_three",
data: [
{data:0,name:"name_three"},
{data:0,name:"name_four"}
]
},
{id: "id_four", name: "name_five"}
]
You can see that some are simple objects, but some have an array of sub-objects, but each one has the key name.
Now, what I want to do is find all the objects with a name field that includes a string.
For example, if I want to find all the objects with name values which include the string "name_", it should return an array of all the objects in array_.
But if I test for the string "name_t", I'd want it to return an array containing the id:two object and the object with id:three, data[0].
Essentially, a search bar.
But I want it to only search for the beginning of the string. So as above, "name_" would return all, but "ame_" would return none.
I don't know if that made any sense, but if It did, I hope someone can help me with this.
Minimal Code:
HTML:
<body>
<input id="text-input" type="text"
</body>
JavaScript (I'm using jQuery):
$('#text-input').on('input', () => {
let inputValue = $('#text-input').val()
function deepSearch (object, key, predicate) {
if (object.hasOwnProperty(key) && predicate(key, object[key]) === true) return object
for (let i = 0; i < Object.keys(object).length; i++) {
if (typeof object[Object.keys(object)[i]] === "object") {
let o = deepSearch(object[Object.keys(object)[i]], key, predicate)
if (o != null) return o
}
}
return null
}
let object_ = deepSearch(array_, "name", (k, v) => v.includes(inputValue))
console.log(object_)
})
This is using a functions I found at this question
This function is almost what I need.
However, it returns the first object with containing the string, whereas I want an array containing all objects that match.
No need for jQuery, necessarily. You can write a recursive function that applies a predicate callback to every node in the tree and keeps the ones that match, flattening arrays as it walks back up the tree. The predicate lets you specify any condition for matching and gives the caller flexibility.
const findNested = (children, predicate, childProp="data") => {
const found = [];
for (const node of children) {
if (node[childProp]) {
found.push(...findNested(node[childProp], predicate));
}
if (predicate(node)) {
found.push(node);
}
}
return found;
};
const tree = [
{id: "id_one", name:"name_one"},
{id: "id_two", name:"name_two"},
{
id:"id_three",
data: [
{data:0,name:"name_three"},
{data:0,name:"name_four"}
]
},
{id: "id_four", name: "name_five"}
];
const predicate = e => e.name && e.name.startsWith("name_t");
console.log(findNested(tree, predicate));
Try this function which recursively searches the object and appends them to an array:
function search(arr, str) {
var ret = [];
for (var i = 0; i < arr.length; i ++) {
if (typeof arr[i].name == "string" && arr[i].name.startsWith(str)) {
ret.push(arr[i]);
} else if (arr[i].data instanceof Array) {
ret = ret.concat(search(arr[i].data, str));
}
}
return ret;
}
search(array_, "name_t"); // array with the 2 matched objects
Related
I have an array res with some nullish values, and I have a function remove that is supposed return an array with nulls and undefined removed, but I can't get it to work on my array. I've seen plenty of answers on this sort of thing, in fact my remove function originated from one of them, but I can't seem to get it to work.
res =
[
{
"1yKKftO0iOyvsacrW1mEr-FylurU8-fwaefewafw": [
"raggy#champ.co",
"grope#champ.co",
null,
"halp#champ.co"
]
},
{
"149Lmt-gweagewfrthjregjiojoinONEDOnonao": [
"raggy#champ.co"
]
},
{
"JG043AHF0GJA0EWJIFJO00WIJF-UffFWEAk8QRg4": [
"wlyman#champ.co",
"raggy#champ.co"
]
},
{
"1u-Frw5I4agI-FWKE0AFJ0WEJG0JEFDALKFEWA-ns": [
null,
"raggy#champ.co"
]
},
{
"FAWGETAIODIOFAIJDSOIFJWEOFijewaofifejowef": [
"raggy#champ.co"
]
},
{
"fwaejf0JF0EWJIJFFJMojfeoijfewJEJFI0i0fje": [
"raggy#champ.co"
]
},
{
"FJ09Ejf093ejfie0jfeiJFEJF0IWJFEIJFOEJWow": [
"raggy#champ.co"
]
}
]
var remove = function (array) {
var result = [];
array.forEach(function (item) {
if (Array.isArray(item) && item.length!=0) {
// Item is a nested array, go one level deeper recursively
result.push(remove(item));
}
else if (typeof item !== null) {
result.push(item);
}
});
return result;
};
console.log(remove(res));
Here is a solution without recursion that will work for the nesting level given in example. Also will work if a single array element has multiple key value pairs.
let res =[{"1yKKftO0iOyvsacrW1mEr-FylurU8-fwaefewafw": ["raggy#champ.co","grope#champ.co",null,"halp#champ.co"]},{"149Lmt-gweagewfrthjregjiojoinONEDOnonao": ["raggy#champ.co"]},{"JG043AHF0GJA0EWJIFJO00WIJF-UffFWEAk8QRg4": ["wlyman#champ.co","raggy#champ.co"]},{"1u-Frw5I4agI-FWKE0AFJ0WEJG0JEFDALKFEWA-ns": [undefined,"raggy#champ.co"]},{"FAWGETAIODIOFAIJDSOIFJWEOFijewaofifejowef": ["raggy#champ.co"]},{"fwaejf0JF0EWJIJFFJMojfeoijfewJEJFI0i0fje": ["raggy#champ.co"]},{"FJ09Ejf093ejfie0jfeiJFEJF0IWJFEIJFOEJWow": ["raggy#champ.co",null]}]
var remove = function (array) {
return array.map(function (item) {
let x = Object.entries(item).map((y) => {
return [y[0],y[1].filter((z)=> z!==undefined && z!==null)]
})
return Object.fromEntries(x)
});
};
console.log(remove(res));
If you want to remove nulls as well as undefineds, you probably want to replace else if (typeof item !== null) with else if (typeof item != null)
What's happening
The elements of res are Objects.
Notice that in the nested function call of remove
result.push(remove(item));
the item being passed are elements of res thus not an array. So when remove(item) is called the check Array.isArray(item) fails and nothing is sorted out.
To get the inner array make add this line.
var values = Object.values(item)
Now handle the cases of item being null, Object and Array.
Solution
Here's my Attempt at the solution. (I hope you don't mind ES6)
This does work on this particular (not sure about other cases)
const remove = (item) => {
if (item) {
console.log('not null', item)
if (Array.isArray(item)) {
const result = []
for (let elem of item) {
const cleanedElem = remove(elem)
// Maybe use Nullish coalescing operator ?
if (cleanedElem !== null && cleanedElem !== undefined)
result.push(cleanedElem)
}
return result
} else if (typeof item === 'string' || typeof item === 'number') {
return item
} else if (item) {
const result = {}
for (let pair of Object.entries(item)) {
const [key, value] = pair
const cleanedValue = remove(value)
// Maybe use Nullish coalescing operator ?
if (cleanedValue !== null && cleanedValue !== undefined)
result[key] = remove(cleanedValue)
}
return result
}
}
}
cleansed = remove(res)
console.log(cleansed);
I am looking to find out an index and group the item belong to in a parent json group, how can I do it?
I am open to reformat the json as well if need be,
I tried JSON.stringify() but it returns the wrong index as well.
let Content = {
group1: [
[{content:"hello"},{content:"world"}],
[{content:"hello1"},{content:"world"}],
[{content:"hello2"},{content:"world"}],
[{content:"hello3"},{content:"world"}],
[{content:"hello4"},{content:"world"}],
[{content:"hello5"},{content:"world"}],
],
group2: [
[{content:"hello10"},{content:"world"}],
[{content:"hello11"},{content:"world"}],
[{content:"hello12"},{content:"world"}],
[{content:"hello13"},{content:"world"}],
[{content:"hello14"},{content:"world"}],
[{content:"hello15"},{content:"world"}],
],
};
// let currentItem = {type:'group2',index:5};
// let currentItemContent = Content[currentItem.type][currentItem.index];
let obj = [{content:"hello15"},{content:"world"}];
let newIndex = Content["group1"].indexOf(obj);
let type = "group1";
if(newIndex < 0)
{
type="group2"
console.log(Content["group2"]);
newIndex = Content["group2"].indexOf(obj);
}
console.log({"type":type,"index":newIndex});
expected: {type:'group2',index:5}
Loop through the Content object using for...in. Check if the given array is in each group by using findIndex. Since both the objects in the array seem to be in order, you can simply compare the string returned by JSON.stringify
let Content={group1:[[{content:"hello"},{content:"world"}],[{content:"hello1"},{content:"world"}],[{content:"hello2"},{content:"world"}],[{content:"hello3"},{content:"world"}],[{content:"hello4"},{content:"world"}],[{content:"hello5"},{content:"world"}]],group2:[[{content:"hello10"},{content:"world"}],[{content:"hello11"},{content:"world"}],[{content:"hello12"},{content:"world"}],[{content:"hello13"},{content:"world"}],[{content:"hello14"},{content:"world"}],[{content:"hello15"},{content:"world"}]]}
function find(input, search) {
for (const type in input) {
const group = input[type];
const index = group.findIndex(a => JSON.stringify(a) === JSON.stringify(search));
if (index != -1)
return { type, index }
}
return null
}
console.log(find(Content, [{content:"hello15"},{content:"world"}]))
console.log(find(Content, [{content:"hello"},{content:"world"}]))
You could also use Array.find in combination with Object.keys and Array.some. The array comparison you can do via JSON.stringify however remember that if your keys are in different order that would not work:
[{content:"world"},{content:"hello"}] vs [{content:"hello"},{content:"world"}]
would not match as you would expect since you are matching on strings and they are now different.
let Content = { group1: [ [{content:"hello"},{content:"world"}], [{content:"hello1"},{content:"world"}], [{content:"hello2"},{content:"world"}], [{content:"hello3"},{content:"world"}], [{content:"hello4"},{content:"world"}], [{content:"hello5"},{content:"world"}], ], group2: [ [{content:"hello10"},{content:"world"}], [{content:"hello11"},{content:"world"}], [{content:"hello12"},{content:"world"}], [{content:"hello13"},{content:"world"}], [{content:"hello14"},{content:"world"}], [{content:"hello15"},{content:"world"}], ], };
let findArray = (data, obj) => {
let index, group = Object.keys(data).find((k,i) => {
index = i
return data[k].some(x => JSON.stringify(x) === JSON.stringify(obj))
})
return { index, group }
}
console.log(findArray(Content, [{content:"hello"},{content:"world"}]))
console.log(findArray(Content, [{content:"hello10"},{content:"world"}]))
In an array of objects with diff keys, how do I find objects by key using ES6 or Lodash?
const arr = [{a:2}, {b:3}, {fred:10}]
I want the result to be:
=> [{a:2}, {fred:10}]
I don't want to use an omit style approach.
const filtered = arr.filter(obj => obj.hasOwnProperty("a") || obj.hasOwnProperty("fred"));
// or, if you have dynamic / lots of keys:
const keys = ["a", "fred"];
const filtered = arr.filter(obj => keys.some(key => obj.hasOwnProperty(key));
filter method will be useful. Create a function and pass an array of keys. Inside filter function check if the key is matching with the parameter array. If it passed then return that object
var orgObject = [{
a: 2
}, {
b: 3
}, {
fred: 10
}];
function searchByKey(keyNames) {
return orgObject.filter(function(item) {
for (var keys in item) {
if (keyNames.indexOf(keys) !== -1) {
return item
}
}
})
}
console.log(searchByKey(['a', 'fred']))
Basically you want all the objects from the array who have the fields a or fred. You can use the hasOwnProperty() on the objects while filtering.
_.filter(array, elem => elem.hasOwnProperty('a') || elem.hasOwnProperty('fred'));
I have an array of objects that I'm trying to loop through to match for a specific value, if found delete that object else return false. Below is the code:
array: [{
obj1:{
id: null,
name:'test',
address: '6857346jfhbdjshb'
},
obj12:{
id: 678346,
name:'test',
address: '63784hgj'
},
obj13:{
id: null,
name:'test',
address: 'tevhgvd'
},
obj15:{
id: 65847,
name:'test',
address: 'djhvwe677ghdgv'
},
obj18:{
address: ""
}
}]
js:
for (var obj in array){
if (array[obj].address === "63784hgj" || array[obj].address === "djhvwe677ghdgv" ||array[prop].address === "")
{
array[obj].destroy(); //equivalent to array[1].destroy() (destroy, delete,remove,hide, pop none of them work)
}
}
I'm not sure which function is the correct one to remove object from array.
You apparently want to filter out certain properties from the single element of your array, which is an object, containing subobjects, when the address property of the subobject matches one or more string values.
Extract the first element of the array, then loop over its properties with a for...in loop. If the subproperty matches, delete the property with delete.
function filterproc(array) {
var o = array[0];
for (var k in o)
if (o[k].address === "....")
delete o[k];
}
To match against multiple possibilities, consider using indexOf, which checks if an array contains some value:
if (['63784hgj', ...].indexOf(o[k]) !== -1) ...
If what you want is what toranaburo described in his answer then nothing to do with mine. :( (inform me to delete mine)
If you want a function which returns the new value do this:
var forbiddenAddressList = ['', '63784hgj', 'djhvwe677ghdgv'];
var newArray = myArray.filter(i => forbiddenAddressList.indexof(i.address) === -1);
another way:
var newArray = [];
for (var i = 0; i < myArray.length; i++)
if(forbiddenAddressList.indexof(i.address) === -1)
newArray.push(myArray[i]);
So if what you're asking is to loop through an array of objects, and if the value of a property is found then delete the object it belongs to, then this is what I came up with.
var array = [
{id: null,name:'test',address: '6857346jfhbdjshb'},
{id: 678346,name:'test',address: '63784hgj'},
{id: null,name:'test',address: 'tevhgvd'},
{id: 65847,name:'test',address: 'djhvwe677ghdgv'},
{address: ""}
];
for (var obj in array){
if (array[obj].address === "63784hgj" || array[obj].address === "djhvwe677ghdgv" || array[obj].address === "")
{
array.splice(obj, 1);
}
}
I modified your array of objects, but everything except Obj1, Obj2 etc. remains because arrays are already indexed, thus array[0] refers to Obj1. Also in your if statement you had array[prop] which is undefined, so be sure to change it in your code. This worked for what I think you were trying to do, so let me know if it helps.
I would like to use reduce on an object with values as strings. If that value doesn't contain the target letter than push that value into an array.
For example:
var animals = {a: 'pig', b: 'pony', c:'possum'};
useReduce(animals, "i"); // returns ['pony', 'possum'];
This is what I have so far, but I'm getting an error:
var useReduce = function(obj, target) {
obj.reduce(function(previousValue, currentValue, key, obj) {
if (currentValue.indexOf(target) === -1) {
previousValue.push(currentValue);
};
}, []);
return previousValue;
};
You can convert the object into an array of keys, then you can reduce. Additionally, previousValue relies on the previous call's return value. You forgot to return inside the reduce.
function useReduce(obj, target) {
return Object.keys(obj).reduce((previousValue, key) => {
if (obj[key].indexOf(target) === -1) previousValue.push(key);
return previousValue;
}, []);
}
As those who have commented have suggested, reduce is an array method, not an object method.
It looks like you are trying to filter the array of objects by certain criteria. Here is one way to accomplish that based on your example using Array.prototype.filter.
This function will return an array of objects only if none of the characters in the name property match the second argument (in this case, i).
var animals = [{name: 'pig'}, {name: 'pony'}, {name:'possum'}];
useReduce(animals, 'i');
function useReduce(arr, str) {
return arr.filter(function(obj) {
if (obj.name.match(str)) {
return false;
} else {
return true;
}
}
}
// returns [{name: 'pony'}, {name:'possum'}];
As an alternative to the answers posted here I just wanted to point out how you can solve this using a functional library, for example with Ramda you can do
const animals = {a: 'pig', b: 'pony', c:'possum'}
const noLetter = R.curry((letter, target) => {
return R.compose(R.reject(R.contains(letter)), R.values)(target)
})
noLetter('i', animals) // ["pony", "possum"]
const noLetterI = noLetter('i')
noLetterI(animals) // ["pony", "possum"]