Skip to content

Instantly share code, notes, and snippets.

@YagoLopez
Last active January 8, 2023 10:02
Show Gist options
  • Save YagoLopez/1c2fe87d255fc64d5f1bf6a920b67484 to your computer and use it in GitHub Desktop.
Save YagoLopez/1c2fe87d255fc64d5f1bf6a920b67484 to your computer and use it in GitHub Desktop.
Deep search javascript object
/* Attribution: http://techslides.com/how-to-parse-and-search-json-in-javascript */
//return an array of objects according to key, value, or key and value matching
function getObjects(obj, key, val) {
var objects = [];
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] == 'object') {
objects = objects.concat(getObjects(obj[i], key, val));
} else
//if key matches and value matches or if key matches and value is not passed (eliminating the case where key matches but passed value does not)
if (i == key && obj[i] == val || i == key && val == '') { //
objects.push(obj);
} else if (obj[i] == val && key == ''){
//only add if the object is not already in the array
if (objects.lastIndexOf(obj) == -1){
objects.push(obj);
}
}
}
return objects;
}
//return an array of values that match on a certain key
function getValues(obj, key) {
var objects = [];
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] == 'object') {
objects = objects.concat(getValues(obj[i], key));
} else if (i == key) {
objects.push(obj[i]);
}
}
return objects;
}
//return an array of keys that match on a certain value
function getKeys(obj, val) {
var objects = [];
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] == 'object') {
objects = objects.concat(getKeys(obj[i], val));
} else if (obj[i] == val) {
objects.push(i);
}
}
return objects;
}
var json = '{"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossList":{"GlossEntry":{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","ID":"44","str":"SGML","GlossSeeAlso":["GML","XML"]},"GlossSee":"markup"}}}}}';
var js = JSON.parse(json);
//example of grabbing objects that match some key and value in JSON
console.log(getObjects(js,'ID','SGML'));
//returns 1 object where a key names ID has the value SGML
//example of grabbing objects that match some key in JSON
console.log(getObjects(js,'ID',''));
//returns 2 objects since keys with name ID are found in 2 objects
//example of grabbing obejcts that match some value in JSON
console.log(getObjects(js,'','SGML'));
//returns 2 object since 2 obects have keys with the value SGML
//example of grabbing objects that match some key in JSON
console.log(getObjects(js,'ID',''));
//returns 2 objects since keys with name ID are found in 2 objects
//example of grabbing values from any key passed in JSON
console.log(getValues(js,'ID'));
//returns array ["SGML", "44"]
//example of grabbing keys by searching via values in JSON
console.log(getKeys(js,'SGML'));
//returns array ["ID", "SortAs", "Acronym", "str"]
@johnclary
Copy link

johnclary commented Aug 18, 2020

thanks @YagoLopez!

here's a modified version that returns an array of strings which represent the path to the matched key (e.g. {key: "ID", "path": ["glossDiv", "glossList", "glossEntry", "ID"]}):

function getKeys(obj, val, path = []) {
  var objects = [];

  for (var i in obj) {
    // clone the path
    var newPath = path.slice();
    if (!obj.hasOwnProperty(i)) {
      continue;
    }
    if (typeof obj[i] === "object") {
      newPath.push(i);
      objects = objects.concat(getKeys(obj[i], val, newPath));
    } else if (obj[i] === val) {
      objects.push({ key: i, path: newPath });
    }
  }
  return objects;
}

@YagoLopez
Copy link
Author

You'r welcome @johnclary. Awesome refactor.

Let me just add a suggestion: I would move this sentence var newPath = path.slice();before the for loop to avoid its execution inside the loop. And using ES6 I'm sure you'll get a much more terse code 👍

@johnclary
Copy link

thanks @YagoLopez. In my testing, path.slice() needs to stay inside the for loop to make a clean break from other obj references to it. will be trying to scratch out an ES6 refactor of this.

@simlu
Copy link

simlu commented Oct 27, 2020

Hey guys! May I recommend you take a look at object-scan. Might be very relevant and exciting 😉 Please do take a look at the source code as well!

https://github.com/blackflux/object-scan

Disclaimer: I'm the author

@YagoLopez
Copy link
Author

Looks great! Thanks for the information

@kyds3k
Copy link

kyds3k commented Jan 11, 2021

How would I go about making this match a wildcard for the key value? For example, my data is:

'{"answer0":"Legend of Zelda","answer1":"Final Fantasy","answer2":"Animal Crossing","answer3":"The Witcher","answer4":"Kingdom Hearts","answer5":"The Sims","artist0":"Wu-Tang Clan","artist1":"Pet Shop Boys","team_id":"79369254","edition_id":"35889464","answerCount":"6","artistCount":"2"}'

I just want the answer* values . . .

@simlu
Copy link

simlu commented Jan 11, 2021

@kyds3k With object-scan you would simply do

const objectScan = require('object-scan');

const data = {
  answer0: 'Legend of Zelda',
  answer1: 'Final Fantasy',
  answer2: 'Animal Crossing',
  answer3: 'The Witcher',
  answer4: 'Kingdom Hearts',
  answer5: 'The Sims',
  artist0: 'Wu-Tang Clan',
  artist1: 'Pet Shop Boys',
  team_id: '79369254',
  edition_id: '35889464',
  answerCount: '6',
  artistCount: '2'
};

console.log(objectScan(['answer*'], { rtn: 'value', reverse: false })(data));
/* => [
  'Legend of Zelda',
  'Final Fantasy',
  'Animal Crossing',
  'The Witcher',
  'Kingdom Hearts',
  'The Sims',
  '6'
] */

console.log(objectScan(['(^answer\\d+$)'], { rtn: 'value', reverse: false })(data));
/* => [
  'Legend of Zelda',
  'Final Fantasy',
  'Animal Crossing',
  'The Witcher',
  'Kingdom Hearts',
  'The Sims'
] */

@YagoLopez
Copy link
Author

Awesome @simlu 👍

@mzcoder-hub
Copy link

@kyds3k With object-scan you would simply do

const objectScan = require('object-scan');

const data = {
  answer0: 'Legend of Zelda',
  answer1: 'Final Fantasy',
  answer2: 'Animal Crossing',
  answer3: 'The Witcher',
  answer4: 'Kingdom Hearts',
  answer5: 'The Sims',
  artist0: 'Wu-Tang Clan',
  artist1: 'Pet Shop Boys',
  team_id: '79369254',
  edition_id: '35889464',
  answerCount: '6',
  artistCount: '2'
};

console.log(objectScan(['answer*'], { rtn: 'value', reverse: false })(data));
/* => [
  'Legend of Zelda',
  'Final Fantasy',
  'Animal Crossing',
  'The Witcher',
  'Kingdom Hearts',
  'The Sims',
  '6'
] */

console.log(objectScan(['(^answer\\d+$)'], { rtn: 'value', reverse: false })(data));
/* => [
  'Legend of Zelda',
  'Final Fantasy',
  'Animal Crossing',
  'The Witcher',
  'Kingdom Hearts',
  'The Sims'
] */

hi i want to try search something using your object scan can that search not fixed value on this object

const data = { merah: [ { harga: 30000, sku: 'SKU-123asd-asdasd', stok: '20', ukuran: 'XL', warna: 'Merah', _id: '6069895a0e277a332ca19e49', }, { harga: 30000, sku: 'SKU-123asd-asdasda', stok: '20', ukuran: 'L', warna: 'Merah', _id: '606b6ea7ff4a142508df2a98', }, ], hijau: [ { harga: 30000, sku: 'SKU-123asd-asdasds', stok: '20', ukuran: 'XL', warna: 'Hijau', _id: '606b6ea7ff4a142508df2a98', }, { harga: 30000, sku: 'SKU-123asd-asdasdsa', stok: '20', ukuran: 'L', warna: 'Hijau', _id: '606b6ea7ff4a142508df2a98', }, ], }

the data will not fixed its will be like another color in english or what ever how do i search depend on the first word like "hijau", "merah"

@simlu
Copy link

simlu commented Apr 6, 2021

Sure, just use ** as a search needle and filter with filterFn using value. It's all in the docs with detailed examples. Let me know if you're struggling

@simlu
Copy link

simlu commented Apr 6, 2021

Or are you trying to search on the key? In that case you could just specify the needle as *? Is that what you're looking for? If you could provide more details with expected output I'd be able to help better

@mzcoder-hub
Copy link

mzcoder-hub commented Apr 6, 2021

Or are you trying to search on the key? In that case you could just specify the needle as *? Is that what you're looking for? If you could provide more details with expected output I'd be able to help better

yeah i try to search at the key so i should use * instead of ** ? ah yeah is it work on ReactJs too ?

@simlu
Copy link

simlu commented Apr 6, 2021

@mzcoder-hub I'd really recommend you take a look at the readme here . It details all the basic use cases and it sound like yours is one of them. Your question is not very clear and so I can't answer it. So you have to options:

  1. go to the readme and figure out what you need to know yourself
  2. reformulate the question in a way that I can understand (ideally with example results!) and I will do my best to provide a working solution

Thank you, L~

@mzcoder-hub
Copy link

@simlu i have this data https://prnt.sc/115tz4e and the key is not fixed its generate from database, and i want to search keyword example "merah" and i want to retrieve data from array "merah"

@simlu
Copy link

simlu commented Apr 6, 2021

Is this what you want?

const objectScan = require('object-scan');

const data = { merah: [{ harga: 30000, sku: 'SKU-123asd-asdasd', stok: '20', ukuran: 'XL', warna: 'Merah', _id: '6069895a0e277a332ca19e49' }, { harga: 30000, sku: 'SKU-123asd-asdasda', stok: '20', ukuran: 'L', warna: 'Merah', _id: '606b6ea7ff4a142508df2a98' }], hijau: [{ harga: 30000, sku: 'SKU-123asd-asdasds', stok: '20', ukuran: 'XL', warna: 'Hijau', _id: '606b6ea7ff4a142508df2a98' }, { harga: 30000, sku: 'SKU-123asd-asdasdsa', stok: '20', ukuran: 'L', warna: 'Hijau', _id: '606b6ea7ff4a142508df2a98' }] };

console.log(objectScan(['*[*]'])(data));
// => [ [ 'hijau', 1 ], [ 'hijau', 0 ], [ 'merah', 1 ], [ 'merah', 0 ] ]

@mzcoder-hub
Copy link

Is this what you want?

const objectScan = require('object-scan');

const data = { merah: [{ harga: 30000, sku: 'SKU-123asd-asdasd', stok: '20', ukuran: 'XL', warna: 'Merah', _id: '6069895a0e277a332ca19e49' }, { harga: 30000, sku: 'SKU-123asd-asdasda', stok: '20', ukuran: 'L', warna: 'Merah', _id: '606b6ea7ff4a142508df2a98' }], hijau: [{ harga: 30000, sku: 'SKU-123asd-asdasds', stok: '20', ukuran: 'XL', warna: 'Hijau', _id: '606b6ea7ff4a142508df2a98' }, { harga: 30000, sku: 'SKU-123asd-asdasdsa', stok: '20', ukuran: 'L', warna: 'Hijau', _id: '606b6ea7ff4a142508df2a98' }] };

console.log(objectScan(['*[*]'])(data));
// => [ [ 'hijau', 1 ], [ 'hijau', 0 ], [ 'merah', 1 ], [ 'merah', 0 ] ]

i want to get data inside of array object

[
    {
      harga: 30000,
      sku: 'SKU-123asd-asdasd',
      stok: '20',
      ukuran: 'XL',
      warna: 'Merah',
      _id: '6069895a0e277a332ca19e49',
    },
    {
      harga: 30000,
      sku: 'SKU-123asd-asdasda',
      stok: '20',
      ukuran: 'L',
      warna: 'Merah',
      _id: '606b6ea7ff4a142508df2a98',
    },
  ],

only single key

if i search 'merah' i only get data from array object from key 'merah'

@simlu
Copy link

simlu commented Apr 6, 2021

Still guessing what you want... how about this?

const objectScan = require('object-scan');

const data = { merah: [{ harga: 30000, sku: 'SKU-123asd-asdasd', stok: '20', ukuran: 'XL', warna: 'Merah', _id: '6069895a0e277a332ca19e49' }, { harga: 30000, sku: 'SKU-123asd-asdasda', stok: '20', ukuran: 'L', warna: 'Merah', _id: '606b6ea7ff4a142508df2a98' }], hijau: [{ harga: 30000, sku: 'SKU-123asd-asdasds', stok: '20', ukuran: 'XL', warna: 'Hijau', _id: '606b6ea7ff4a142508df2a98' }, { harga: 30000, sku: 'SKU-123asd-asdasdsa', stok: '20', ukuran: 'L', warna: 'Hijau', _id: '606b6ea7ff4a142508df2a98' }] };

const r = objectScan(['*[*]'], { rtn: 'value' })(data);
console.log(JSON.stringify(r));
// => [{"harga":30000,"sku":"SKU-123asd-asdasdsa","stok":"20","ukuran":"L","warna":"Hijau","_id":"606b6ea7ff4a142508df2a98"},{"harga":30000,"sku":"SKU-123asd-asdasds","stok":"20","ukuran":"XL","warna":"Hijau","_id":"606b6ea7ff4a142508df2a98"},{"harga":30000,"sku":"SKU-123asd-asdasda","stok":"20","ukuran":"L","warna":"Merah","_id":"606b6ea7ff4a142508df2a98"},{"harga":30000,"sku":"SKU-123asd-asdasd","stok":"20","ukuran":"XL","warna":"Merah","_id":"6069895a0e277a332ca19e49"}]

@mzcoder-hub
Copy link

Still guessing what you want... how about this?

const objectScan = require('object-scan');

const data = { merah: [{ harga: 30000, sku: 'SKU-123asd-asdasd', stok: '20', ukuran: 'XL', warna: 'Merah', _id: '6069895a0e277a332ca19e49' }, { harga: 30000, sku: 'SKU-123asd-asdasda', stok: '20', ukuran: 'L', warna: 'Merah', _id: '606b6ea7ff4a142508df2a98' }], hijau: [{ harga: 30000, sku: 'SKU-123asd-asdasds', stok: '20', ukuran: 'XL', warna: 'Hijau', _id: '606b6ea7ff4a142508df2a98' }, { harga: 30000, sku: 'SKU-123asd-asdasdsa', stok: '20', ukuran: 'L', warna: 'Hijau', _id: '606b6ea7ff4a142508df2a98' }] };

const r = objectScan(['*[*]'], { rtn: 'value' })(data);
console.log(JSON.stringify(r));
// => [{"harga":30000,"sku":"SKU-123asd-asdasdsa","stok":"20","ukuran":"L","warna":"Hijau","_id":"606b6ea7ff4a142508df2a98"},{"harga":30000,"sku":"SKU-123asd-asdasds","stok":"20","ukuran":"XL","warna":"Hijau","_id":"606b6ea7ff4a142508df2a98"},{"harga":30000,"sku":"SKU-123asd-asdasda","stok":"20","ukuran":"L","warna":"Merah","_id":"606b6ea7ff4a142508df2a98"},{"harga":30000,"sku":"SKU-123asd-asdasd","stok":"20","ukuran":"XL","warna":"Merah","_id":"6069895a0e277a332ca19e49"}]

Im fixing it using the script above

console.log(getObjects(js,'','SGML'));

@androcado
Copy link

@YagoLopez Thanks! Exactly what I need and it works great

@YagoLopez
Copy link
Author

YagoLopez commented Jul 14, 2022

@YagoLopez Thanks! Exactly what I need and it works great

You´re welcome @androcado. Please consider give a start to the gist if you found it useful. Thanks 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment