0

I have the following dataset:

var data = [
    {
        "raw_title": "Test A",
        "raw_description": "Description",
        "url": "test_url",
        "id": 360000710653,
        "key": "test_key1"
    },
    {
        "raw_title": "Test A",
        "raw_description": "Description",
        "url": "test_url",
        "id": 360000710654,
        "key": "test_key2"
    },
    {
        "raw_title": "Test A",
        "raw_description": "Description",
        "url": "test_url",
        "id": 360000710655,
        "key": "test_key3"
    },
    {
        "raw_title": "Test A",
        "raw_description": "Description",
        "url": "test_url",
        "id": 360000710656,
        "key": "test_key4"
    }]

I need to be able to search through this array and return the key where the ID is equal to a certain ID.

For example, I need the object keys where ID = 360000710654 OR ID = 360000710655.

Therefore, pseudo code =

ID = 360000710654 OR ID = 360000710655 would return:
key: "test_key2",
key: "test_key3"

This is a small example, but I will need this to work on much larger datasets with about 40 different IDs that I need to pull the key from. In addition, finding a way to change the key name for the key/value pair would be great so I could easily identify which key is what.

I have tried making this work with a JS filter function, as I cannot use any outside functions but have been unsuccessful so far. I am stuck and unsure where to take this and would love some additional help.

I tried this but my output is always null.

var data = [{
    "raw_title": "Test A",
    "raw_description": "Description",
    "url": "test_url",
    "id": 360000710653,
    "key": "test_key1"
  },
  {
    "raw_title": "Test A",
    "raw_description": "Description",
    "url": "test_url",
    "id": 360000710654,
    "key": "test_key2"
  },
  {
    "raw_title": "Test A",
    "raw_description": "Description",
    "url": "test_url",
    "id": 360000710655,
    "key": "test_key3"
  },
  {
    "raw_title": "Test A",
    "raw_description": "Description",
    "url": "test_url",
    "id": 360000710656,
    "key": "test_key4"
  }
];
var result = data.filter(obj => {
  return obj.id === 360000710654;
});
console.log(result);

This works, and returns the data in the order it is found.

    let data = data
    .filter(item => [360001060233, 360000710653].includes(item.id))
    .map(item => item.key)
  return data;

Update

I am trying to have it return in the order I search for it.

This snippet is based on the comments to the answer from Mr. Polywhirl:

const keysForIds = (ids, data) => 
  Object.fromEntries(
    ids .map ((target, _, __, item = data. find (({id}) => id == target) || {}) => [target, item.key]
  ))

const data = [{"id": 360000710653, "key": "test_key1", "raw_description": "Description", "raw_title": "Test A", "url": "test_url"}, {"id": 360000710654, "key": "test_key2", "raw_description": "Description", "raw_title": "Test A", "url": "test_url"}, {"id": 360000710655, "key": "test_key3", "raw_description": "Description", "raw_title": "Test A", "url": "test_url"}, {"id": 360000710656, "key": "test_key4", "raw_description": "Description", "raw_title": "Test A", "url": "test_url"}]

console .log (
  keysForIds([360000710654, 360000710658, 360000710655], data)
)
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103

1 Answers1

3

All you need to do is filter and map the items.

Find the ones that have the id that you are looking for first, then return the matching item keys.

let data = getData()
  .filter(item => [360000710654, 360000710655].includes(item.id))
  .map(item => item.key)

console.log(data)

function getData() {
  return [{
    "raw_title": "Test A",
    "raw_description": "Description",
    "url": "test_url",
    "id": 360000710653,
    "key": "test_key1"
  }, {
    "raw_title": "Test A",
    "raw_description": "Description",
    "url": "test_url",
    "id": 360000710654,
    "key": "test_key2"
  }, {
    "raw_title": "Test A",
    "raw_description": "Description",
    "url": "test_url",
    "id": 360000710655,
    "key": "test_key3"
  }, {
    "raw_title": "Test A",
    "raw_description": "Description",
    "url": "test_url",
    "id": 360000710656,
    "key": "test_key4"
  }]
}

Alternately, you could create a function.

const filterMap = (data, values, inKey, outKey) => {
  return data.filter(item => values.includes(item[inKey])).map(item => item[outKey])
};

console.log(filterMap(getData(), [360000710654, 360000710655], 'id', 'key'))

Edit:

So it looks like you want to preserve order of the input ids. Well for this you simple reverse the lookup.

let data = getData()
let keys = [360000710655, 360000710654]
  .map(id => (found => found ? found.key : null)(data.find(item => item.id === id)))

And here is the altered function.

const filterMap = (data, values, inKey, outKey) => {
  return values.map(v => (m => m ? m[outKey] : null)(data.find(e => e[inKey] === v)))
};

Where:

v = the current id
m = the matching item (can be null)
e = current item in the data we are testing to see if the id matches
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
  • If you're going to write a `filterMap` function, wouldn't it be better if it accepted functions as predicates, something like `const filterMap = (test, transform) => (data) => data .filter (test) .map (transform)`, which might here be used here as `const idKeys = (ids) => filterMap (({id}) => ids.includes(id), ({key}) => key)`, which in turn would be called like `const results = idKeys ([360000710654, 360000710654]) (data) //=> ["test_key2", "test_key4"]`? – Scott Sauyet Dec 03 '19 at 18:55
  • Okay, I got this code to work, but I am struggling on what should be easy which is returning the data in GetData from a variable rather than directly from an array so I can pass the data in from another source. Thoughts? – Dakota Brown Dec 03 '19 at 18:58
  • 1
    @ScottSauyet As predicates are the way to go, the the function is just mis-named... It should be renamed "retrieveOutputValuesForMatchingInputValues" or something like that... – Mr. Polywhirl Dec 03 '19 at 18:59
  • 1
    @DakotaBrown `getData()` is just my way of forward declaration... I wanted to keep the logic at the top so you don't have to scroll to the bottom... You can really just ignore it. – Mr. Polywhirl Dec 03 '19 at 18:59
  • Quick question, when filtering the items, will it always return or console log them in the order they are entered as parameters? i.e. Will it always return the key value for 360000710654 first, before returning 360000710655? – Dakota Brown Dec 03 '19 at 19:11
  • Answered my own question, looks like it just outputs in the order it finds them. – Dakota Brown Dec 03 '19 at 19:19
  • The order is solely determined by original item order. – Mr. Polywhirl Dec 03 '19 at 19:19
  • Is there any way to change that, so I can control/change the order that it returns in? – Dakota Brown Dec 03 '19 at 19:21
  • So you want a sorting function to be passed in? – Mr. Polywhirl Dec 03 '19 at 19:35
  • 1
    ... or are you just trying to return the keys in the order of the ids supplied? If that's the case, then a different technique might be better. – Scott Sauyet Dec 03 '19 at 19:36
  • I want to return the keys in the order of the ids supplied. I need them to return exactly the same everytime as I am looking for specific keys that may or not have the same key name, they may be slightly different, but by staying in the order I searched for them in, I know that even if the name is different, the output returns in the same order I searched for, and therefore I can associate slightly different keys with the same ID. – Dakota Brown Dec 03 '19 at 19:39
  • 1
    Then something like this might do: `const keysForIds = (ids, data) => ids .map ((target) => data. find (({id}) => id == target)) .map ((o => o && o.key))`. If you want to remove the `undefined` for those that are missing, you can tack on `.filter (Boolean)` -- but then you have an alignment issue. – Scott Sauyet Dec 03 '19 at 19:43
  • 1
    I would suggest, though, that something like this would return a more useful structure: `const keysForIds = (ids, data) => Object.fromEntries(ids .map ((target, _, __, item = data. find (({id}) => id == target) || {}) => [target, item.key]))` – Scott Sauyet Dec 03 '19 at 19:48
  • Hmm, okay, undefined is perfect if fields are not found because I can search for that and have it notify me if something is missing. Would your code replace the map filtering or just be in addition to the code, i.e. where does it fit in. Apologies for the hand holding here, some of this is a new concept for me. – Dakota Brown Dec 03 '19 at 19:49
  • 1
    Each of my previous two comments have stand-alone code. (They could be called like `keysForIds ([360000710654, 360000710658, 360000710655], data)`) The latter one is what I would recommend, as a dictionary is a far better data structure than two arrays which have to align on indices. It returns something like `{ 360000710654: "test_key2", 360000710658: undefined, 360000710655: "test_key3" }` – Scott Sauyet Dec 03 '19 at 19:50
  • @ScottSauyet I think I am just stuck in my head at this point but I can't for the life of me get your code above to work, I want to wrap the const keysForIds into a function right? And then call that? I added the code to the bottom of my main post above if you want to show me there. – Dakota Brown Dec 03 '19 at 20:14
  • 1
    @DakotaBrown: Updated your question to include this technique. Note that `keysForIds` *is* a function. It doesn't need to be wrapped in anything. – Scott Sauyet Dec 03 '19 at 20:25